mirror of
https://github.com/lua/lua.git
synced 2026-07-27 00:19:07 +00:00
Compare commits
386 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cfce6f4b20 | ||
|
|
1da89da62f | ||
|
|
f6c627af20 | ||
|
|
270a58c062 | ||
|
|
30531c291b | ||
|
|
07b009c371 | ||
|
|
f711567448 | ||
|
|
9386e49a31 | ||
|
|
8cd7ae7da0 | ||
|
|
0cecf1ab6d | ||
|
|
e657a48ea5 | ||
|
|
fd897027f1 | ||
|
|
d05fe48bfd | ||
|
|
519c57d597 | ||
|
|
c15543b9af | ||
|
|
be05c44481 | ||
|
|
6d53701c7a | ||
|
|
abbae57c78 | ||
|
|
f2c1531e6c | ||
|
|
ded2ad2d86 | ||
|
|
3fb7a77731 | ||
|
|
fded0b4a84 | ||
|
|
3b9dd52be0 | ||
|
|
7dc6aae290 | ||
|
|
5b1ab8efdc | ||
|
|
7ade155762 | ||
|
|
d827e96f33 | ||
|
|
3f0ea90aa8 | ||
|
|
4365a45d68 | ||
|
|
be81209063 | ||
|
|
e055905914 | ||
|
|
9b014d4bcd | ||
|
|
50fd8d03c3 | ||
|
|
3dbb1a4b89 | ||
|
|
3dd8ea54da | ||
|
|
620f49a7aa | ||
|
|
3f4f28010a | ||
|
|
93e347b519 | ||
|
|
f4123b2fc2 | ||
|
|
37a1b72706 | ||
|
|
ef5d171cc8 | ||
|
|
b0f3df16a4 | ||
|
|
cad5a4fdbb | ||
|
|
fdbb4c2341 | ||
|
|
921832be8d | ||
|
|
205f9aa67b | ||
|
|
94d38560c3 | ||
|
|
c2dc6e8e94 | ||
|
|
22974326ca | ||
|
|
c931d86e98 | ||
|
|
d9e0f64a5d | ||
|
|
ab66652b32 | ||
|
|
4398e488e6 | ||
|
|
808976bb59 | ||
|
|
b5b1995f29 | ||
|
|
cb88c1cd5d | ||
|
|
ee99452158 | ||
|
|
127a8e80fe | ||
|
|
f9e35627ed | ||
|
|
ceac82f78b | ||
|
|
e5f4927a0b | ||
|
|
cd38fe8cf3 | ||
|
|
fa1382b5cd | ||
|
|
d1e677c52b | ||
|
|
f7439112a5 | ||
|
|
39a14ea7d7 | ||
|
|
c4e7cdb541 | ||
|
|
7d7ae8781e | ||
|
|
724012d3d0 | ||
|
|
664bda02ba | ||
|
|
2d8d5c74b5 | ||
|
|
3cdd49c94a | ||
|
|
10e931da82 | ||
|
|
4b107a8776 | ||
|
|
429761d7d2 | ||
|
|
915c29f8bd | ||
|
|
7ca3c40b50 | ||
|
|
8a3a49250c | ||
|
|
1ec251e091 | ||
|
|
5894ca7b95 | ||
|
|
abf8b1cd4a | ||
|
|
2a307f898b | ||
|
|
f81d0bbd4f | ||
|
|
1c40ff9faa | ||
|
|
7538f3886d | ||
|
|
412e9a4d95 | ||
|
|
25a491fe34 | ||
|
|
b4b616bdf2 | ||
|
|
bb93f04d87 | ||
|
|
975d4e0592 | ||
|
|
04e495403b | ||
|
|
62afbc6bfc | ||
|
|
002beeebe7 | ||
|
|
9329eeac3b | ||
|
|
682efe2678 | ||
|
|
50c7c915ee | ||
|
|
b117bdb344 | ||
|
|
f12ce4029d | ||
|
|
a4762b6ffe | ||
|
|
d4247befa1 | ||
|
|
ee6a4cd1ec | ||
|
|
8a4419b119 | ||
|
|
9a91fe1640 | ||
|
|
2491b87c10 | ||
|
|
0de8191152 | ||
|
|
853311e5b1 | ||
|
|
25a2dac2bc | ||
|
|
e3ce88c9e8 | ||
|
|
5ffcd458f0 | ||
|
|
9b01da97e3 | ||
|
|
258355734d | ||
|
|
d0815046d0 | ||
|
|
3d54b42d59 | ||
|
|
e4f418f07c | ||
|
|
20d42ccaae | ||
|
|
70d6975018 | ||
|
|
00e34375ec | ||
|
|
45a8f1b593 | ||
|
|
8fac494509 | ||
|
|
9b72355f99 | ||
|
|
ddfa1fbccf | ||
|
|
b443145ff3 | ||
|
|
4901853c11 | ||
|
|
a04e0ffdb9 | ||
|
|
007b8c7a01 | ||
|
|
fd0e1f530d | ||
|
|
75620b45ae | ||
|
|
3e88b72b8e | ||
|
|
8b752ddf14 | ||
|
|
1bf4b80f1a | ||
|
|
4c6afbcb01 | ||
|
|
f2206b2abe | ||
|
|
0acd55898d | ||
|
|
15231d4fb2 | ||
|
|
f407b3c4a1 | ||
|
|
a546138d15 | ||
|
|
cd4de92762 | ||
|
|
6b45ccf4ed | ||
|
|
93fd6892f8 | ||
|
|
193bf7919e | ||
|
|
366c855648 | ||
|
|
781219dbe1 | ||
|
|
d71fbc3175 | ||
|
|
c403e456b6 | ||
|
|
6ac7219da3 | ||
|
|
9904c253da | ||
|
|
fb7e5b76c9 | ||
|
|
c1dc08e8e8 | ||
|
|
0f7025dcae | ||
|
|
ef28e5f789 | ||
|
|
ec65ab878e | ||
|
|
e24ce8c2b3 | ||
|
|
a08d82eb13 | ||
|
|
55ac40f859 | ||
|
|
97ef8e7bd4 | ||
|
|
aaf3533653 | ||
|
|
b529aefc53 | ||
|
|
d51022bf9e | ||
|
|
bb7bb5944c | ||
|
|
94b503d95e | ||
|
|
bdc85357aa | ||
|
|
b291008cc2 | ||
|
|
814213b65f | ||
|
|
cbdf4969ec | ||
|
|
262dc5729a | ||
|
|
9d985db7bb | ||
|
|
0897c0a428 | ||
|
|
5edacafcfa | ||
|
|
3507c3380f | ||
|
|
88a50ffa71 | ||
|
|
86a8e74824 | ||
|
|
9fa63a6268 | ||
|
|
0593256707 | ||
|
|
ce6f5502c9 | ||
|
|
ba71060381 | ||
|
|
3823fc6c81 | ||
|
|
52aa2b5d24 | ||
|
|
cc2b66c856 | ||
|
|
65b07dd53d | ||
|
|
7237eb3f1c | ||
|
|
165389b27b | ||
|
|
c8121ce34b | ||
|
|
7360f8d0fd | ||
|
|
6063c47031 | ||
|
|
0c9bec0d38 | ||
|
|
c31d6774ac | ||
|
|
108e0bdc84 | ||
|
|
b34a97a4af | ||
|
|
3e9dbe143d | ||
|
|
4a8e480864 | ||
|
|
17e0c29d9b | ||
|
|
8eb0abc9db | ||
|
|
d862da6d04 | ||
|
|
e288c5a918 | ||
|
|
7827c40c49 | ||
|
|
e7af9cdf0b | ||
|
|
12b6f610b0 | ||
|
|
e81f586001 | ||
|
|
e2cc179454 | ||
|
|
5853c37a83 | ||
|
|
ad0ea7813b | ||
|
|
666e95a66d | ||
|
|
4eda1acafa | ||
|
|
ad73b33224 | ||
|
|
925fe8a0f2 | ||
|
|
789e7acdea | ||
|
|
74b4013538 | ||
|
|
b719ff9399 | ||
|
|
35a2fed2d1 | ||
|
|
63d68bd657 | ||
|
|
842a83f09c | ||
|
|
011850a8f8 | ||
|
|
52b899d60d | ||
|
|
25cd3d377e | ||
|
|
1028f296a8 | ||
|
|
6d042a178f | ||
|
|
eabf425c76 | ||
|
|
3b57e37e48 | ||
|
|
024f9064f1 | ||
|
|
7f4906f565 | ||
|
|
b8a9d14032 | ||
|
|
19afd91687 | ||
|
|
37c215b43f | ||
|
|
9e99f3071d | ||
|
|
fa075b7953 | ||
|
|
08a077d673 | ||
|
|
7923dbbf72 | ||
|
|
43c8e5bded | ||
|
|
b8b709b6d4 | ||
|
|
81e4fce530 | ||
|
|
6baee9ef9d | ||
|
|
edd8589f47 | ||
|
|
14e416355f | ||
|
|
f33cda8d6e | ||
|
|
96f7714237 | ||
|
|
0554581605 | ||
|
|
07a9eab23a | ||
|
|
c815c2f0eb | ||
|
|
9363a8b990 | ||
|
|
5ab6a5756b | ||
|
|
9b4f39ab14 | ||
|
|
f4211a5ea4 | ||
|
|
1b3f507f62 | ||
|
|
6b51133a98 | ||
|
|
cbae016202 | ||
|
|
ab6a949522 | ||
|
|
ea39042e13 | ||
|
|
05ec55f16b | ||
|
|
f623b96932 | ||
|
|
9be74ccc21 | ||
|
|
819bd51d87 | ||
|
|
f8d30826dd | ||
|
|
351ccd7332 | ||
|
|
09f3c2372f | ||
|
|
c197885cb0 | ||
|
|
934e77a286 | ||
|
|
6443185167 | ||
|
|
e15f1f2bb7 | ||
|
|
b5c65705ca | ||
|
|
7ca8105a2e | ||
|
|
94689ac3ad | ||
|
|
86e8039a72 | ||
|
|
5a04f1851e | ||
|
|
ab859fe59b | ||
|
|
c4b71b7ba0 | ||
|
|
1de2f31694 | ||
|
|
02bab9fc25 | ||
|
|
5e08b41567 | ||
|
|
8c064fdc23 | ||
|
|
cf08915d62 | ||
|
|
c888ae0aea | ||
|
|
d69789da1c | ||
|
|
8dea54877a | ||
|
|
b5ab31a475 | ||
|
|
314745ed84 | ||
|
|
140cdcced5 | ||
|
|
0825cf237d | ||
|
|
7d4c7ae2af | ||
|
|
873588dc5f | ||
|
|
b2f7b3b79f | ||
|
|
540d805226 | ||
|
|
7d6a97e42b | ||
|
|
d70a0c91ad | ||
|
|
3e6818ca87 | ||
|
|
f874d37fa2 | ||
|
|
88e5c6b80b | ||
|
|
35e01ed21d | ||
|
|
e33e1bda97 | ||
|
|
6aabf4b15e | ||
|
|
5d8b5b9290 | ||
|
|
40565b4a08 | ||
|
|
ff106c028c | ||
|
|
fa2f294dd1 | ||
|
|
d738c8d18b | ||
|
|
0270c204c2 | ||
|
|
82fae58e25 | ||
|
|
efc7c5d503 | ||
|
|
d324a0ccf9 | ||
|
|
152b51955a | ||
|
|
ec61be9a7e | ||
|
|
f356d5acdd | ||
|
|
9a77f57edc | ||
|
|
be908a7d4d | ||
|
|
76953316d1 | ||
|
|
3d2bd1359d | ||
|
|
9ede317c70 | ||
|
|
8047b2d03e | ||
|
|
ee645472eb | ||
|
|
413a393e62 | ||
|
|
ba089bcb08 | ||
|
|
1e64c1391f | ||
|
|
b85816b9a8 | ||
|
|
c954db3924 | ||
|
|
14d2803e55 | ||
|
|
7f12bf40c4 | ||
|
|
26be27459b | ||
|
|
cfbe378f90 | ||
|
|
a1089b415a | ||
|
|
f8c4c4fcf2 | ||
|
|
71bc69c2af | ||
|
|
cd56f222b7 | ||
|
|
69b77b6fde | ||
|
|
997f11f543 | ||
|
|
02060b7a37 | ||
|
|
a1f77a234a | ||
|
|
c6cea857a4 | ||
|
|
d61b0c6028 | ||
|
|
196bb94d66 | ||
|
|
603b2c64ad | ||
|
|
4a00f61276 | ||
|
|
42d40581dd | ||
|
|
e435aaabef | ||
|
|
315639d3bb | ||
|
|
c764ca71a6 | ||
|
|
295cde9454 | ||
|
|
f3cfd5bf2b | ||
|
|
8426d9b4d4 | ||
|
|
1f3c6f4534 | ||
|
|
25b143dd34 | ||
|
|
5d708c3f9c | ||
|
|
8dd2c912d2 | ||
|
|
05ac2409ee | ||
|
|
597a53bbc6 | ||
|
|
86ec152433 | ||
|
|
cf613cdc6f | ||
|
|
066e0f93c4 | ||
|
|
0bfc572e51 | ||
|
|
1de95e97ef | ||
|
|
ad3942adba | ||
|
|
48835c76c8 | ||
|
|
6b3e116d44 | ||
|
|
e8deac5a41 | ||
|
|
bfbff3703e | ||
|
|
74d99057a5 | ||
|
|
1fce5bea81 | ||
|
|
3699446aaf | ||
|
|
0e5071b5fb | ||
|
|
87a9573b2e | ||
|
|
deac067ed3 | ||
|
|
2ff3471722 | ||
|
|
9db4bfed6b | ||
|
|
91673a8ec0 | ||
|
|
41871f1803 | ||
|
|
a393ac2554 | ||
|
|
65434b4d1b | ||
|
|
59acd79c05 | ||
|
|
e2c07dcbf7 | ||
|
|
439e45a2f6 | ||
|
|
62fb934427 | ||
|
|
8a32e0aa4a | ||
|
|
dbdc74dc55 | ||
|
|
6a0dace25a | ||
|
|
04e19712a5 | ||
|
|
901d760093 | ||
|
|
c0ed74c1e1 | ||
|
|
fc6c74f100 | ||
|
|
1e6918d553 | ||
|
|
681297187e | ||
|
|
5148954eed | ||
|
|
d205f3a484 | ||
|
|
47cffdc723 | ||
|
|
36de01d988 | ||
|
|
7fbe215808 | ||
|
|
bf10593a3a | ||
|
|
bef250eb8d | ||
|
|
ba81adaad9 |
2
all
2
all
@@ -1,7 +1,7 @@
|
||||
make -s -j
|
||||
cd testes/libs; make -s
|
||||
cd .. # back to directory 'testes'
|
||||
ulimit -S -s 1000
|
||||
ulimit -S -s 1100
|
||||
if { ../lua -W all.lua; } then
|
||||
echo -e "\n\n final OK!!!!\n\n"
|
||||
else
|
||||
|
||||
56
lapi.h
56
lapi.h
@@ -12,38 +12,54 @@
|
||||
#include "lstate.h"
|
||||
|
||||
|
||||
/* Increments 'L->top', checking for stack overflows */
|
||||
#define api_incr_top(L) {L->top++; api_check(L, L->top <= L->ci->top, \
|
||||
"stack overflow");}
|
||||
#if defined(LUA_USE_APICHECK)
|
||||
#include <assert.h>
|
||||
#define api_check(l,e,msg) assert(e)
|
||||
#else /* for testing */
|
||||
#define api_check(l,e,msg) ((void)(l), lua_assert((e) && msg))
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Increments 'L->top.p', checking for stack overflows */
|
||||
#define api_incr_top(L) \
|
||||
(L->top.p++, api_check(L, L->top.p <= L->ci->top.p, "stack overflow"))
|
||||
|
||||
|
||||
/*
|
||||
** macros that are executed whenever program enters the Lua core
|
||||
** ('lua_lock') and leaves the core ('lua_unlock')
|
||||
*/
|
||||
#if !defined(lua_lock)
|
||||
#define lua_lock(L) ((void) 0)
|
||||
#define lua_unlock(L) ((void) 0)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** If a call returns too many multiple returns, the callee may not have
|
||||
** stack space to accommodate all results. In this case, this macro
|
||||
** increases its stack space ('L->ci->top').
|
||||
** increases its stack space ('L->ci->top.p').
|
||||
*/
|
||||
#define adjustresults(L,nres) \
|
||||
{ if ((nres) <= LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; }
|
||||
{ if ((nres) <= LUA_MULTRET && L->ci->top.p < L->top.p) \
|
||||
L->ci->top.p = L->top.p; }
|
||||
|
||||
|
||||
/* 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")
|
||||
#define api_checknelems(L,n) \
|
||||
api_check(L, (n) < (L->top.p - L->ci->func.p), \
|
||||
"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.
|
||||
/* Ensure the stack has at least 'n' elements to be popped. (Some
|
||||
** functions only update a slot after checking it for popping, but that
|
||||
** is only an optimization for a pop followed by a push.)
|
||||
*/
|
||||
|
||||
#define hastocloseCfunc(n) ((n) < LUA_MULTRET)
|
||||
|
||||
/* Map [-1, inf) (range of 'nresults') into (-inf, -2] */
|
||||
#define codeNresults(n) (-(n) - 3)
|
||||
#define decodeNresults(n) (-(n) - 3)
|
||||
#define api_checkpop(L,n) \
|
||||
api_check(L, (n) < L->top.p - L->ci->func.p && \
|
||||
L->tbclist.p < L->top.p - (n), \
|
||||
"not enough free elements in the stack")
|
||||
|
||||
#endif
|
||||
|
||||
273
lauxlib.c
273
lauxlib.c
@@ -25,12 +25,7 @@
|
||||
#include "lua.h"
|
||||
|
||||
#include "lauxlib.h"
|
||||
|
||||
|
||||
#if !defined(MAX_SIZET)
|
||||
/* maximum value for size_t */
|
||||
#define MAX_SIZET ((size_t)(~(size_t)0))
|
||||
#endif
|
||||
#include "llimits.h"
|
||||
|
||||
|
||||
/*
|
||||
@@ -80,6 +75,7 @@ static int pushglobalfuncname (lua_State *L, lua_Debug *ar) {
|
||||
int top = lua_gettop(L);
|
||||
lua_getinfo(L, "f", ar); /* push function */
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
|
||||
luaL_checkstack(L, 6, "not enough stack"); /* slots for 'findfield' */
|
||||
if (findfield(L, top + 1, 2)) {
|
||||
const char *name = lua_tostring(L, -1);
|
||||
if (strncmp(name, LUA_GNAME ".", 3) == 0) { /* name start with '_G.'? */
|
||||
@@ -98,14 +94,14 @@ static int pushglobalfuncname (lua_State *L, lua_Debug *ar) {
|
||||
|
||||
|
||||
static void pushfuncname (lua_State *L, lua_Debug *ar) {
|
||||
if (pushglobalfuncname(L, ar)) { /* try first a global name */
|
||||
lua_pushfstring(L, "function '%s'", lua_tostring(L, -1));
|
||||
lua_remove(L, -2); /* remove name */
|
||||
}
|
||||
else if (*ar->namewhat != '\0') /* is there a name from code? */
|
||||
if (*ar->namewhat != '\0') /* is there a name from code? */
|
||||
lua_pushfstring(L, "%s '%s'", ar->namewhat, ar->name); /* use it */
|
||||
else if (*ar->what == 'm') /* main? */
|
||||
lua_pushliteral(L, "main chunk");
|
||||
else if (pushglobalfuncname(L, ar)) { /* try a global name */
|
||||
lua_pushfstring(L, "function '%s'", lua_tostring(L, -1));
|
||||
lua_remove(L, -2); /* remove name */
|
||||
}
|
||||
else if (*ar->what != 'C') /* for Lua functions, use <file:line> */
|
||||
lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined);
|
||||
else /* nothing left... */
|
||||
@@ -174,19 +170,27 @@ LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1,
|
||||
|
||||
LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) {
|
||||
lua_Debug ar;
|
||||
const char *argword;
|
||||
if (!lua_getstack(L, 0, &ar)) /* no stack frame? */
|
||||
return luaL_error(L, "bad argument #%d (%s)", arg, extramsg);
|
||||
lua_getinfo(L, "n", &ar);
|
||||
if (strcmp(ar.namewhat, "method") == 0) {
|
||||
arg--; /* do not count 'self' */
|
||||
if (arg == 0) /* error is in the self argument itself? */
|
||||
return luaL_error(L, "calling '%s' on bad self (%s)",
|
||||
ar.name, extramsg);
|
||||
lua_getinfo(L, "nt", &ar);
|
||||
if (arg <= ar.extraargs) /* error in an extra argument? */
|
||||
argword = "extra argument";
|
||||
else {
|
||||
arg -= ar.extraargs; /* do not count extra arguments */
|
||||
if (strcmp(ar.namewhat, "method") == 0) { /* colon syntax? */
|
||||
arg--; /* do not count (extra) self argument */
|
||||
if (arg == 0) /* error in self argument? */
|
||||
return luaL_error(L, "calling '%s' on bad self (%s)",
|
||||
ar.name, extramsg);
|
||||
/* else go through; error in a regular argument */
|
||||
}
|
||||
argword = "argument";
|
||||
}
|
||||
if (ar.name == NULL)
|
||||
ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?";
|
||||
return luaL_error(L, "bad argument #%d to '%s' (%s)",
|
||||
arg, ar.name, extramsg);
|
||||
return luaL_error(L, "bad %s #%d to '%s' (%s)",
|
||||
argword, arg, ar.name, extramsg);
|
||||
}
|
||||
|
||||
|
||||
@@ -229,7 +233,7 @@ LUALIB_API void luaL_where (lua_State *L, int level) {
|
||||
/*
|
||||
** Again, the use of 'lua_pushvfstring' ensures this function does
|
||||
** not need reserved stack space when called. (At worst, it generates
|
||||
** an error with "stack overflow" instead of the given message.)
|
||||
** a memory error instead of the given message.)
|
||||
*/
|
||||
LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) {
|
||||
va_list argp;
|
||||
@@ -249,11 +253,13 @@ LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
const char *msg;
|
||||
luaL_pushfail(L);
|
||||
msg = (en != 0) ? strerror(en) : "(no extra info)";
|
||||
if (fname)
|
||||
lua_pushfstring(L, "%s: %s", fname, strerror(en));
|
||||
lua_pushfstring(L, "%s: %s", fname, msg);
|
||||
else
|
||||
lua_pushstring(L, strerror(en));
|
||||
lua_pushstring(L, msg);
|
||||
lua_pushinteger(L, en);
|
||||
return 3;
|
||||
}
|
||||
@@ -470,18 +476,27 @@ typedef struct UBox {
|
||||
} UBox;
|
||||
|
||||
|
||||
/* Resize the buffer used by a box. Optimize for the common case of
|
||||
** resizing to the old size. (For instance, __gc will resize the box
|
||||
** to 0 even after it was closed. 'pushresult' may also resize it to a
|
||||
** final size that is equal to the one set when the buffer was created.)
|
||||
*/
|
||||
static void *resizebox (lua_State *L, int idx, size_t newsize) {
|
||||
void *ud;
|
||||
lua_Alloc allocf = lua_getallocf(L, &ud);
|
||||
UBox *box = (UBox *)lua_touserdata(L, idx);
|
||||
void *temp = allocf(ud, box->box, box->bsize, newsize);
|
||||
if (l_unlikely(temp == NULL && newsize > 0)) { /* allocation error? */
|
||||
lua_pushliteral(L, "not enough memory");
|
||||
lua_error(L); /* raise a memory error */
|
||||
if (box->bsize == newsize) /* not changing size? */
|
||||
return box->box; /* keep the buffer */
|
||||
else {
|
||||
void *ud;
|
||||
lua_Alloc allocf = lua_getallocf(L, &ud);
|
||||
void *temp = allocf(ud, box->box, box->bsize, newsize);
|
||||
if (l_unlikely(temp == NULL && newsize > 0)) { /* allocation error? */
|
||||
lua_pushliteral(L, "not enough memory");
|
||||
lua_error(L); /* raise a memory error */
|
||||
}
|
||||
box->box = temp;
|
||||
box->bsize = newsize;
|
||||
return temp;
|
||||
}
|
||||
box->box = temp;
|
||||
box->bsize = newsize;
|
||||
return temp;
|
||||
}
|
||||
|
||||
|
||||
@@ -526,14 +541,17 @@ static void newbox (lua_State *L) {
|
||||
|
||||
/*
|
||||
** Compute new size for buffer 'B', enough to accommodate extra 'sz'
|
||||
** bytes.
|
||||
** bytes plus one for a terminating zero.
|
||||
*/
|
||||
static size_t newbuffsize (luaL_Buffer *B, size_t sz) {
|
||||
size_t newsize = B->size * 2; /* double buffer size */
|
||||
if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */
|
||||
return luaL_error(B->L, "buffer too large");
|
||||
if (newsize < B->n + sz) /* double is not big enough? */
|
||||
newsize = B->n + sz;
|
||||
size_t newsize = B->size;
|
||||
if (l_unlikely(sz >= MAX_SIZE - B->n))
|
||||
return cast_sizet(luaL_error(B->L, "resulting string too large"));
|
||||
/* else B->n + sz + 1 <= MAX_SIZE */
|
||||
if (newsize <= MAX_SIZE/3 * 2) /* no overflow? */
|
||||
newsize += (newsize >> 1); /* new size *= 1.5 */
|
||||
if (newsize < B->n + sz + 1) /* not big enough? */
|
||||
newsize = B->n + sz + 1;
|
||||
return newsize;
|
||||
}
|
||||
|
||||
@@ -593,9 +611,23 @@ LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) {
|
||||
LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
|
||||
lua_State *L = B->L;
|
||||
checkbufferlevel(B, -1);
|
||||
lua_pushlstring(L, B->b, B->n);
|
||||
if (buffonstack(B))
|
||||
if (!buffonstack(B)) /* using static buffer? */
|
||||
lua_pushlstring(L, B->b, B->n); /* save result as regular string */
|
||||
else { /* reuse buffer already allocated */
|
||||
UBox *box = (UBox *)lua_touserdata(L, -1);
|
||||
void *ud;
|
||||
lua_Alloc allocf = lua_getallocf(L, &ud); /* function to free buffer */
|
||||
size_t len = B->n; /* final string length */
|
||||
char *s;
|
||||
resizebox(L, -1, len + 1); /* adjust box size to content size */
|
||||
s = (char*)box->box; /* final buffer address */
|
||||
s[len] = '\0'; /* add ending zero */
|
||||
/* clear box, as Lua will take control of the buffer */
|
||||
box->bsize = 0; box->box = NULL;
|
||||
lua_pushexternalstring(L, s, len, allocf, ud);
|
||||
lua_closeslot(L, -2); /* close the box */
|
||||
lua_gc(L, LUA_GCSTEP, len);
|
||||
}
|
||||
lua_remove(L, -2); /* remove box or placeholder from the stack */
|
||||
}
|
||||
|
||||
@@ -611,7 +643,7 @@ LUALIB_API void luaL_pushresultsize (luaL_Buffer *B, size_t sz) {
|
||||
** 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
|
||||
** be) below 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.)
|
||||
*/
|
||||
@@ -649,13 +681,10 @@ LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) {
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
/* index of free-list header (after the predefined values) */
|
||||
#define freelist (LUA_RIDX_LAST + 1)
|
||||
|
||||
/*
|
||||
** The previously freed references form a linked list:
|
||||
** t[freelist] is the index of a first free index, or zero if list is
|
||||
** empty; t[t[freelist]] is the index of the second element; etc.
|
||||
** The previously freed references form a linked list: t[1] is the index
|
||||
** of a first free index, t[t[1]] is the index of the second element,
|
||||
** etc. A zero signals the end of the list.
|
||||
*/
|
||||
LUALIB_API int luaL_ref (lua_State *L, int t) {
|
||||
int ref;
|
||||
@@ -664,19 +693,18 @@ LUALIB_API int luaL_ref (lua_State *L, int t) {
|
||||
return LUA_REFNIL; /* 'nil' has a unique fixed reference */
|
||||
}
|
||||
t = lua_absindex(L, t);
|
||||
if (lua_rawgeti(L, t, freelist) == LUA_TNIL) { /* first access? */
|
||||
if (lua_rawgeti(L, t, 1) == LUA_TNUMBER) /* already initialized? */
|
||||
ref = (int)lua_tointeger(L, -1); /* ref = t[1] */
|
||||
else { /* first access */
|
||||
lua_assert(!lua_toboolean(L, -1)); /* must be nil or false */
|
||||
ref = 0; /* list is empty */
|
||||
lua_pushinteger(L, 0); /* initialize as an empty list */
|
||||
lua_rawseti(L, t, freelist); /* ref = t[freelist] = 0 */
|
||||
}
|
||||
else { /* already initialized */
|
||||
lua_assert(lua_isinteger(L, -1));
|
||||
ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */
|
||||
lua_rawseti(L, t, 1); /* ref = t[1] = 0 */
|
||||
}
|
||||
lua_pop(L, 1); /* remove element from stack */
|
||||
if (ref != 0) { /* any free element? */
|
||||
lua_rawgeti(L, t, ref); /* remove it from list */
|
||||
lua_rawseti(L, t, freelist); /* (t[freelist] = t[ref]) */
|
||||
lua_rawseti(L, t, 1); /* (t[1] = t[ref]) */
|
||||
}
|
||||
else /* no free elements */
|
||||
ref = (int)lua_rawlen(L, t) + 1; /* get a new reference */
|
||||
@@ -688,11 +716,11 @@ LUALIB_API int luaL_ref (lua_State *L, int t) {
|
||||
LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
|
||||
if (ref >= 0) {
|
||||
t = lua_absindex(L, t);
|
||||
lua_rawgeti(L, t, freelist);
|
||||
lua_rawgeti(L, t, 1);
|
||||
lua_assert(lua_isinteger(L, -1));
|
||||
lua_rawseti(L, t, ref); /* t[ref] = t[freelist] */
|
||||
lua_rawseti(L, t, ref); /* t[ref] = t[1] */
|
||||
lua_pushinteger(L, ref);
|
||||
lua_rawseti(L, t, freelist); /* t[freelist] = ref */
|
||||
lua_rawseti(L, t, 1); /* t[1] = ref */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -706,7 +734,7 @@ LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
|
||||
*/
|
||||
|
||||
typedef struct LoadF {
|
||||
int n; /* number of pre-read characters */
|
||||
unsigned n; /* number of pre-read characters */
|
||||
FILE *f; /* file being read */
|
||||
char buff[BUFSIZ]; /* area for reading file */
|
||||
} LoadF;
|
||||
@@ -731,25 +759,29 @@ static const char *getF (lua_State *L, void *ud, size_t *size) {
|
||||
|
||||
|
||||
static int errfile (lua_State *L, const char *what, int fnameindex) {
|
||||
const char *serr = strerror(errno);
|
||||
int err = errno;
|
||||
const char *filename = lua_tostring(L, fnameindex) + 1;
|
||||
lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr);
|
||||
if (err != 0)
|
||||
lua_pushfstring(L, "cannot %s %s: %s", what, filename, strerror(err));
|
||||
else
|
||||
lua_pushfstring(L, "cannot %s %s", what, filename);
|
||||
lua_remove(L, fnameindex);
|
||||
return LUA_ERRFILE;
|
||||
}
|
||||
|
||||
|
||||
static int skipBOM (LoadF *lf) {
|
||||
const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */
|
||||
int c;
|
||||
lf->n = 0;
|
||||
do {
|
||||
c = getc(lf->f);
|
||||
if (c == EOF || c != *(const unsigned char *)p++) return c;
|
||||
lf->buff[lf->n++] = c; /* to be read by the parser */
|
||||
} while (*p != '\0');
|
||||
lf->n = 0; /* prefix matched; discard it */
|
||||
return getc(lf->f); /* return next character */
|
||||
/*
|
||||
** Skip an optional BOM at the start of a stream. If there is an
|
||||
** incomplete BOM (the first character is correct but the rest is
|
||||
** not), returns the first character anyway to force an error
|
||||
** (as no chunk can start with 0xEF).
|
||||
*/
|
||||
static int skipBOM (FILE *f) {
|
||||
int c = getc(f); /* read first character */
|
||||
if (c == 0xEF && getc(f) == 0xBB && getc(f) == 0xBF) /* correct BOM? */
|
||||
return getc(f); /* ignore BOM and return next char */
|
||||
else /* no (valid) BOM */
|
||||
return c; /* return first character */
|
||||
}
|
||||
|
||||
|
||||
@@ -760,13 +792,13 @@ static int skipBOM (LoadF *lf) {
|
||||
** first "valid" character of the file (after the optional BOM and
|
||||
** a first-line comment).
|
||||
*/
|
||||
static int skipcomment (LoadF *lf, int *cp) {
|
||||
int c = *cp = skipBOM(lf);
|
||||
static int skipcomment (FILE *f, int *cp) {
|
||||
int c = *cp = skipBOM(f);
|
||||
if (c == '#') { /* first line is a comment (Unix exec. file)? */
|
||||
do { /* skip first line */
|
||||
c = getc(lf->f);
|
||||
c = getc(f);
|
||||
} while (c != EOF && c != '\n');
|
||||
*cp = getc(lf->f); /* skip end-of-line, if present */
|
||||
*cp = getc(f); /* next character after comment, if present */
|
||||
return 1; /* there was a comment */
|
||||
}
|
||||
else return 0; /* no comment */
|
||||
@@ -785,20 +817,27 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
|
||||
}
|
||||
else {
|
||||
lua_pushfstring(L, "@%s", filename);
|
||||
errno = 0;
|
||||
lf.f = fopen(filename, "r");
|
||||
if (lf.f == NULL) return errfile(L, "open", fnameindex);
|
||||
}
|
||||
if (skipcomment(&lf, &c)) /* read initial portion */
|
||||
lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */
|
||||
if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
|
||||
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
|
||||
if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
|
||||
skipcomment(&lf, &c); /* re-read initial portion */
|
||||
lf.n = 0;
|
||||
if (skipcomment(lf.f, &c)) /* read initial portion */
|
||||
lf.buff[lf.n++] = '\n'; /* add newline to correct line numbers */
|
||||
if (c == LUA_SIGNATURE[0]) { /* binary file? */
|
||||
lf.n = 0; /* remove possible newline */
|
||||
if (filename) { /* "real" file? */
|
||||
errno = 0;
|
||||
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
|
||||
if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
|
||||
skipcomment(lf.f, &c); /* re-read initial portion */
|
||||
}
|
||||
}
|
||||
if (c != EOF)
|
||||
lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */
|
||||
lf.buff[lf.n++] = cast_char(c); /* 'c' is the first character */
|
||||
status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode);
|
||||
readstatus = ferror(lf.f);
|
||||
errno = 0; /* no useful error number until here */
|
||||
if (filename) fclose(lf.f); /* close file (even in case of errors) */
|
||||
if (readstatus) {
|
||||
lua_settop(L, fnameindex); /* ignore results from 'lua_load' */
|
||||
@@ -881,6 +920,7 @@ LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) {
|
||||
|
||||
|
||||
LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
|
||||
idx = lua_absindex(L,idx);
|
||||
if (luaL_callmeta(L, idx, "__tostring")) { /* metafield? */
|
||||
if (!lua_isstring(L, -1))
|
||||
luaL_error(L, "'__tostring' must return a string");
|
||||
@@ -888,10 +928,9 @@ LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
|
||||
else {
|
||||
switch (lua_type(L, idx)) {
|
||||
case LUA_TNUMBER: {
|
||||
if (lua_isinteger(L, idx))
|
||||
lua_pushfstring(L, "%I", (LUAI_UACINT)lua_tointeger(L, idx));
|
||||
else
|
||||
lua_pushfstring(L, "%f", (LUAI_UACNUMBER)lua_tonumber(L, idx));
|
||||
char buff[LUA_N2SBUFFSZ];
|
||||
lua_numbertocstring(L, idx, buff);
|
||||
lua_pushstring(L, buff);
|
||||
break;
|
||||
}
|
||||
case LUA_TSTRING:
|
||||
@@ -926,7 +965,7 @@ LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
|
||||
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 */
|
||||
if (l->func == NULL) /* place holder? */
|
||||
if (l->func == NULL) /* placeholder? */
|
||||
lua_pushboolean(L, 0);
|
||||
else {
|
||||
int i;
|
||||
@@ -989,7 +1028,7 @@ LUALIB_API void luaL_addgsub (luaL_Buffer *b, const char *s,
|
||||
const char *wild;
|
||||
size_t l = strlen(p);
|
||||
while ((wild = strstr(s, p)) != NULL) {
|
||||
luaL_addlstring(b, s, wild - s); /* push prefix */
|
||||
luaL_addlstring(b, s, ct_diff2sz(wild - s)); /* push prefix */
|
||||
luaL_addstring(b, r); /* push replacement in place of pattern */
|
||||
s = wild + l; /* continue after 'p' */
|
||||
}
|
||||
@@ -1018,9 +1057,14 @@ static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Standard panic function just prints an error message. The test
|
||||
** with 'lua_type' avoids possible memory errors in 'lua_tostring'.
|
||||
*/
|
||||
static int panic (lua_State *L) {
|
||||
const char *msg = lua_tostring(L, -1);
|
||||
if (msg == NULL) msg = "error object is not a string";
|
||||
const char *msg = (lua_type(L, -1) == LUA_TSTRING)
|
||||
? lua_tostring(L, -1)
|
||||
: "error object is not a string";
|
||||
lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n",
|
||||
msg);
|
||||
return 0; /* return to Lua to abort */
|
||||
@@ -1084,8 +1128,57 @@ static void warnfon (void *ud, const char *message, int tocont) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** A function to compute an unsigned int with some level of
|
||||
** randomness. Rely on Address Space Layout Randomization (if present)
|
||||
** and the current time.
|
||||
*/
|
||||
#if !defined(luai_makeseed)
|
||||
|
||||
#include <time.h>
|
||||
|
||||
|
||||
/* Size for the buffer, in bytes */
|
||||
#define BUFSEEDB (sizeof(void*) + sizeof(time_t))
|
||||
|
||||
/* Size for the buffer in int's, rounded up */
|
||||
#define BUFSEED ((BUFSEEDB + sizeof(int) - 1) / sizeof(int))
|
||||
|
||||
/*
|
||||
** Copy the contents of variable 'v' into the buffer pointed by 'b'.
|
||||
** (The '&b[0]' disguises 'b' to fix an absurd warning from clang.)
|
||||
*/
|
||||
#define addbuff(b,v) (memcpy(&b[0], &(v), sizeof(v)), b += sizeof(v))
|
||||
|
||||
|
||||
static unsigned int luai_makeseed (void) {
|
||||
unsigned int buff[BUFSEED];
|
||||
unsigned int res;
|
||||
unsigned int i;
|
||||
time_t t = time(NULL);
|
||||
char *b = (char*)buff;
|
||||
addbuff(b, b); /* local variable's address */
|
||||
addbuff(b, t); /* time */
|
||||
/* fill (rare but possible) remain of the buffer with zeros */
|
||||
memset(b, 0, sizeof(buff) - BUFSEEDB);
|
||||
res = buff[0];
|
||||
for (i = 1; i < BUFSEED; i++)
|
||||
res ^= (res >> 3) + (res << 7) + buff[i];
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
LUALIB_API unsigned int luaL_makeseed (lua_State *L) {
|
||||
(void)L; /* unused */
|
||||
return luai_makeseed();
|
||||
}
|
||||
|
||||
|
||||
LUALIB_API lua_State *luaL_newstate (void) {
|
||||
lua_State *L = lua_newstate(l_alloc, NULL);
|
||||
lua_State *L = lua_newstate(l_alloc, NULL, luai_makeseed());
|
||||
if (l_likely(L)) {
|
||||
lua_atpanic(L, &panic);
|
||||
lua_setwarnf(L, warnfoff, L); /* default is warnings off */
|
||||
|
||||
49
lauxlib.h
49
lauxlib.h
@@ -100,9 +100,11 @@ LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
|
||||
|
||||
LUALIB_API lua_State *(luaL_newstate) (void);
|
||||
|
||||
LUALIB_API unsigned luaL_makeseed (lua_State *L);
|
||||
|
||||
LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);
|
||||
|
||||
LUALIB_API void luaL_addgsub (luaL_Buffer *b, const char *s,
|
||||
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);
|
||||
@@ -154,22 +156,19 @@ 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)
|
||||
|
||||
|
||||
/*
|
||||
** Internal assertions for in-house debugging
|
||||
** Perform arithmetic operations on lua_Integer values with wrap-around
|
||||
** semantics, as the Lua core does.
|
||||
*/
|
||||
#if !defined(lua_assert)
|
||||
#define luaL_intop(op,v1,v2) \
|
||||
((lua_Integer)((lua_Unsigned)(v1) op (lua_Unsigned)(v2)))
|
||||
|
||||
#if defined LUAI_ASSERT
|
||||
#include <assert.h>
|
||||
#define lua_assert(c) assert(c)
|
||||
|
||||
/* push the value used to represent failure/error */
|
||||
#if defined(LUA_FAILISFALSE)
|
||||
#define luaL_pushfail(L) lua_pushboolean(L, 0)
|
||||
#else
|
||||
#define lua_assert(c) ((void)0)
|
||||
#endif
|
||||
|
||||
#define luaL_pushfail(L) lua_pushnil(L)
|
||||
#endif
|
||||
|
||||
|
||||
@@ -241,30 +240,6 @@ typedef struct luaL_Stream {
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** "Abstraction Layer" for basic report of messages and errors
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/* print a string */
|
||||
#if !defined(lua_writestring)
|
||||
#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout)
|
||||
#endif
|
||||
|
||||
/* print a newline and flush the output */
|
||||
#if !defined(lua_writeline)
|
||||
#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout))
|
||||
#endif
|
||||
|
||||
/* print an error message */
|
||||
#if !defined(lua_writestringerror)
|
||||
#define lua_writestringerror(s,p) \
|
||||
(fprintf(stderr, (s), (p)), fflush(stderr))
|
||||
#endif
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {============================================================
|
||||
|
||||
98
lbaselib.c
98
lbaselib.c
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
#include "llimits.h"
|
||||
|
||||
|
||||
static int luaB_print (lua_State *L) {
|
||||
@@ -57,21 +58,22 @@ static int luaB_warn (lua_State *L) {
|
||||
|
||||
#define SPACECHARS " \f\n\r\t\v"
|
||||
|
||||
static const char *b_str2int (const char *s, int base, lua_Integer *pn) {
|
||||
static const char *b_str2int (const char *s, unsigned base, lua_Integer *pn) {
|
||||
lua_Unsigned n = 0;
|
||||
int neg = 0;
|
||||
s += strspn(s, SPACECHARS); /* skip initial spaces */
|
||||
if (*s == '-') { s++; neg = 1; } /* handle sign */
|
||||
else if (*s == '+') s++;
|
||||
if (!isalnum((unsigned char)*s)) /* no digit? */
|
||||
if (!isalnum(cast_uchar(*s))) /* no digit? */
|
||||
return NULL;
|
||||
do {
|
||||
int digit = (isdigit((unsigned char)*s)) ? *s - '0'
|
||||
: (toupper((unsigned char)*s) - 'A') + 10;
|
||||
unsigned digit = cast_uint(isdigit(cast_uchar(*s))
|
||||
? *s - '0'
|
||||
: (toupper(cast_uchar(*s)) - 'A') + 10);
|
||||
if (digit >= base) return NULL; /* invalid numeral */
|
||||
n = n * base + digit;
|
||||
s++;
|
||||
} while (isalnum((unsigned char)*s));
|
||||
} while (isalnum(cast_uchar(*s)));
|
||||
s += strspn(s, SPACECHARS); /* skip trailing spaces */
|
||||
*pn = (lua_Integer)((neg) ? (0u - n) : n);
|
||||
return s;
|
||||
@@ -101,7 +103,7 @@ static int luaB_tonumber (lua_State *L) {
|
||||
luaL_checktype(L, 1, LUA_TSTRING); /* no numbers as strings */
|
||||
s = lua_tolstring(L, 1, &l);
|
||||
luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range");
|
||||
if (b_str2int(s, (int)base, &n) == s + l) {
|
||||
if (b_str2int(s, cast_uint(base), &n) == s + l) {
|
||||
lua_pushinteger(L, n);
|
||||
return 1;
|
||||
} /* else not a number */
|
||||
@@ -158,7 +160,7 @@ static int luaB_rawlen (lua_State *L) {
|
||||
int t = lua_type(L, 1);
|
||||
luaL_argexpected(L, t == LUA_TTABLE || t == LUA_TSTRING, 1,
|
||||
"table or string");
|
||||
lua_pushinteger(L, lua_rawlen(L, 1));
|
||||
lua_pushinteger(L, l_castU2S(lua_rawlen(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -182,62 +184,76 @@ static int luaB_rawset (lua_State *L) {
|
||||
|
||||
|
||||
static int pushmode (lua_State *L, int oldmode) {
|
||||
lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental"
|
||||
: "generational");
|
||||
if (oldmode == -1)
|
||||
luaL_pushfail(L); /* invalid call to 'lua_gc' */
|
||||
else
|
||||
lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental"
|
||||
: "generational");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** check whether call to 'lua_gc' was valid (not inside a finalizer)
|
||||
*/
|
||||
#define checkvalres(res) { if (res == -1) break; }
|
||||
|
||||
static int luaB_collectgarbage (lua_State *L) {
|
||||
static const char *const opts[] = {"stop", "restart", "collect",
|
||||
"count", "step", "setpause", "setstepmul",
|
||||
"isrunning", "generational", "incremental", NULL};
|
||||
static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,
|
||||
LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL,
|
||||
LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC};
|
||||
"count", "step", "isrunning", "generational", "incremental",
|
||||
"param", NULL};
|
||||
static const char optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,
|
||||
LUA_GCCOUNT, LUA_GCSTEP, LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC,
|
||||
LUA_GCPARAM};
|
||||
int o = optsnum[luaL_checkoption(L, 1, "collect", opts)];
|
||||
switch (o) {
|
||||
case LUA_GCCOUNT: {
|
||||
int k = lua_gc(L, o);
|
||||
int b = lua_gc(L, LUA_GCCOUNTB);
|
||||
checkvalres(k);
|
||||
lua_pushnumber(L, (lua_Number)k + ((lua_Number)b/1024));
|
||||
return 1;
|
||||
}
|
||||
case LUA_GCSTEP: {
|
||||
int step = (int)luaL_optinteger(L, 2, 0);
|
||||
int res = lua_gc(L, o, step);
|
||||
lua_Integer n = luaL_optinteger(L, 2, 0);
|
||||
int res = lua_gc(L, o, cast_sizet(n));
|
||||
checkvalres(res);
|
||||
lua_pushboolean(L, res);
|
||||
return 1;
|
||||
}
|
||||
case LUA_GCSETPAUSE:
|
||||
case LUA_GCSETSTEPMUL: {
|
||||
int p = (int)luaL_optinteger(L, 2, 0);
|
||||
int previous = lua_gc(L, o, p);
|
||||
lua_pushinteger(L, previous);
|
||||
return 1;
|
||||
}
|
||||
case LUA_GCISRUNNING: {
|
||||
int res = lua_gc(L, o);
|
||||
checkvalres(res);
|
||||
lua_pushboolean(L, res);
|
||||
return 1;
|
||||
}
|
||||
case LUA_GCGEN: {
|
||||
int minormul = (int)luaL_optinteger(L, 2, 0);
|
||||
int majormul = (int)luaL_optinteger(L, 3, 0);
|
||||
return pushmode(L, lua_gc(L, o, minormul, majormul));
|
||||
return pushmode(L, lua_gc(L, o));
|
||||
}
|
||||
case LUA_GCINC: {
|
||||
int pause = (int)luaL_optinteger(L, 2, 0);
|
||||
int stepmul = (int)luaL_optinteger(L, 3, 0);
|
||||
int stepsize = (int)luaL_optinteger(L, 4, 0);
|
||||
return pushmode(L, lua_gc(L, o, pause, stepmul, stepsize));
|
||||
return pushmode(L, lua_gc(L, o));
|
||||
}
|
||||
case LUA_GCPARAM: {
|
||||
static const char *const params[] = {
|
||||
"minormul", "majorminor", "minormajor",
|
||||
"pause", "stepmul", "stepsize", NULL};
|
||||
static const char pnum[] = {
|
||||
LUA_GCPMINORMUL, LUA_GCPMAJORMINOR, LUA_GCPMINORMAJOR,
|
||||
LUA_GCPPAUSE, LUA_GCPSTEPMUL, LUA_GCPSTEPSIZE};
|
||||
int p = pnum[luaL_checkoption(L, 2, NULL, params)];
|
||||
lua_Integer value = luaL_optinteger(L, 3, -1);
|
||||
lua_pushinteger(L, lua_gc(L, o, p, (int)value));
|
||||
return 1;
|
||||
}
|
||||
default: {
|
||||
int res = lua_gc(L, o);
|
||||
checkvalres(res);
|
||||
lua_pushinteger(L, res);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
luaL_pushfail(L); /* invalid call (inside a finalizer) */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -261,6 +277,11 @@ static int luaB_next (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
static int pairscont (lua_State *L, int status, lua_KContext k) {
|
||||
(void)L; (void)status; (void)k; /* unused */
|
||||
return 3;
|
||||
}
|
||||
|
||||
static int luaB_pairs (lua_State *L) {
|
||||
luaL_checkany(L, 1);
|
||||
if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */
|
||||
@@ -270,7 +291,7 @@ static int luaB_pairs (lua_State *L) {
|
||||
}
|
||||
else {
|
||||
lua_pushvalue(L, 1); /* argument 'self' to metamethod */
|
||||
lua_call(L, 1, 3); /* get 3 values from metamethod */
|
||||
lua_callk(L, 1, 3, 0, pairscont); /* get 3 values from metamethod */
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
@@ -280,7 +301,8 @@ static int luaB_pairs (lua_State *L) {
|
||||
** Traversal function for 'ipairs'
|
||||
*/
|
||||
static int ipairsaux (lua_State *L) {
|
||||
lua_Integer i = luaL_checkinteger(L, 2) + 1;
|
||||
lua_Integer i = luaL_checkinteger(L, 2);
|
||||
i = luaL_intop(+, i, 1);
|
||||
lua_pushinteger(L, i);
|
||||
return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2;
|
||||
}
|
||||
@@ -316,9 +338,17 @@ static int load_aux (lua_State *L, int status, int envidx) {
|
||||
}
|
||||
|
||||
|
||||
static const char *getMode (lua_State *L, int idx) {
|
||||
const char *mode = luaL_optstring(L, idx, "bt");
|
||||
if (strchr(mode, 'B') != NULL) /* Lua code cannot use fixed buffers */
|
||||
luaL_argerror(L, idx, "invalid mode");
|
||||
return mode;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_loadfile (lua_State *L) {
|
||||
const char *fname = luaL_optstring(L, 1, NULL);
|
||||
const char *mode = luaL_optstring(L, 2, NULL);
|
||||
const char *mode = getMode(L, 2);
|
||||
int env = (!lua_isnone(L, 3) ? 3 : 0); /* 'env' index or 0 if no 'env' */
|
||||
int status = luaL_loadfilex(L, fname, mode);
|
||||
return load_aux(L, status, env);
|
||||
@@ -367,7 +397,7 @@ static int luaB_load (lua_State *L) {
|
||||
int status;
|
||||
size_t l;
|
||||
const char *s = lua_tolstring(L, 1, &l);
|
||||
const char *mode = luaL_optstring(L, 3, "bt");
|
||||
const char *mode = getMode(L, 3);
|
||||
int env = (!lua_isnone(L, 4) ? 4 : 0); /* 'env' index or 0 if no 'env' */
|
||||
if (s != NULL) { /* loading a string? */
|
||||
const char *chunkname = luaL_optstring(L, 2, s);
|
||||
|
||||
441
lcode.c
441
lcode.c
@@ -10,6 +10,7 @@
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <float.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
@@ -30,10 +31,7 @@
|
||||
#include "lvm.h"
|
||||
|
||||
|
||||
/* Maximum number of registers in a Lua function (must fit in 8 bits) */
|
||||
#define MAXREGS 255
|
||||
|
||||
|
||||
/* (note that expressions VJMP also have jumps.) */
|
||||
#define hasjumps(e) ((e)->t != (e)->f)
|
||||
|
||||
|
||||
@@ -42,7 +40,10 @@ static int codesJ (FuncState *fs, OpCode o, int sj, int k);
|
||||
|
||||
|
||||
/* semantic error */
|
||||
l_noret luaK_semerror (LexState *ls, const char *msg) {
|
||||
l_noret luaK_semerror (LexState *ls, const char *fmt, ...) {
|
||||
const char *msg;
|
||||
va_list argp;
|
||||
pushvfstring(ls->L, argp, fmt, msg);
|
||||
ls->t.token = 0; /* remove "near <token>" from final message */
|
||||
luaX_syntaxerror(ls, msg);
|
||||
}
|
||||
@@ -210,6 +211,7 @@ void luaK_ret (FuncState *fs, int first, int nret) {
|
||||
case 1: op = OP_RETURN1; break;
|
||||
default: op = OP_RETURN; break;
|
||||
}
|
||||
luaY_checklimit(fs, nret + 1, MAXARG_B, "returns");
|
||||
luaK_codeABC(fs, op, first, nret + 1, 0);
|
||||
}
|
||||
|
||||
@@ -330,15 +332,15 @@ static void savelineinfo (FuncState *fs, Proto *f, int line) {
|
||||
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->sizeabslineinfo, AbsLineInfo, INT_MAX, "lines");
|
||||
f->abslineinfo[fs->nabslineinfo].pc = pc;
|
||||
f->abslineinfo[fs->nabslineinfo++].line = line;
|
||||
linedif = ABSLINEINFO; /* signal that there is absolute information */
|
||||
fs->iwthabs = 1; /* restart counter */
|
||||
}
|
||||
luaM_growvector(fs->ls->L, f->lineinfo, pc, f->sizelineinfo, ls_byte,
|
||||
MAX_INT, "opcodes");
|
||||
f->lineinfo[pc] = linedif;
|
||||
INT_MAX, "opcodes");
|
||||
f->lineinfo[pc] = cast(ls_byte, linedif);
|
||||
fs->previousline = line; /* last line saved */
|
||||
}
|
||||
|
||||
@@ -382,7 +384,7 @@ 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");
|
||||
INT_MAX, "opcodes");
|
||||
f->code[fs->pc++] = i;
|
||||
savelineinfo(fs, f, fs->ls->lastline);
|
||||
return fs->pc - 1; /* index of new instruction */
|
||||
@@ -393,32 +395,40 @@ int luaK_code (FuncState *fs, Instruction i) {
|
||||
** Format and emit an 'iABC' instruction. (Assertions check consistency
|
||||
** of parameters versus opcode.)
|
||||
*/
|
||||
int luaK_codeABCk (FuncState *fs, OpCode o, int a, int b, int c, int k) {
|
||||
int luaK_codeABCk (FuncState *fs, OpCode o, int A, int B, int C, int k) {
|
||||
lua_assert(getOpMode(o) == iABC);
|
||||
lua_assert(a <= MAXARG_A && b <= MAXARG_B &&
|
||||
c <= MAXARG_C && (k & ~1) == 0);
|
||||
return luaK_code(fs, CREATE_ABCk(o, a, b, c, k));
|
||||
lua_assert(A <= MAXARG_A && B <= MAXARG_B &&
|
||||
C <= MAXARG_C && (k & ~1) == 0);
|
||||
return luaK_code(fs, CREATE_ABCk(o, A, B, C, k));
|
||||
}
|
||||
|
||||
|
||||
int luaK_codevABCk (FuncState *fs, OpCode o, int A, int B, int C, int k) {
|
||||
lua_assert(getOpMode(o) == ivABC);
|
||||
lua_assert(A <= MAXARG_A && B <= MAXARG_vB &&
|
||||
C <= MAXARG_vC && (k & ~1) == 0);
|
||||
return luaK_code(fs, CREATE_vABCk(o, A, B, C, k));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Format and emit an 'iABx' instruction.
|
||||
*/
|
||||
int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) {
|
||||
int luaK_codeABx (FuncState *fs, OpCode o, int A, int Bc) {
|
||||
lua_assert(getOpMode(o) == iABx);
|
||||
lua_assert(a <= MAXARG_A && bc <= MAXARG_Bx);
|
||||
return luaK_code(fs, CREATE_ABx(o, a, bc));
|
||||
lua_assert(A <= MAXARG_A && Bc <= MAXARG_Bx);
|
||||
return luaK_code(fs, CREATE_ABx(o, A, Bc));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Format and emit an 'iAsBx' instruction.
|
||||
*/
|
||||
int luaK_codeAsBx (FuncState *fs, OpCode o, int a, int bc) {
|
||||
unsigned int b = bc + OFFSET_sBx;
|
||||
static int codeAsBx (FuncState *fs, OpCode o, int A, int Bc) {
|
||||
int b = Bc + OFFSET_sBx;
|
||||
lua_assert(getOpMode(o) == iAsBx);
|
||||
lua_assert(a <= MAXARG_A && b <= MAXARG_Bx);
|
||||
return luaK_code(fs, CREATE_ABx(o, a, b));
|
||||
lua_assert(A <= MAXARG_A && b <= MAXARG_Bx);
|
||||
return luaK_code(fs, CREATE_ABx(o, A, b));
|
||||
}
|
||||
|
||||
|
||||
@@ -426,7 +436,7 @@ int luaK_codeAsBx (FuncState *fs, OpCode o, int a, int bc) {
|
||||
** Format and emit an 'isJ' instruction.
|
||||
*/
|
||||
static int codesJ (FuncState *fs, OpCode o, int sj, int k) {
|
||||
unsigned int j = sj + OFFSET_sJ;
|
||||
int j = sj + OFFSET_sJ;
|
||||
lua_assert(getOpMode(o) == isJ);
|
||||
lua_assert(j <= MAXARG_sJ && (k & ~1) == 0);
|
||||
return luaK_code(fs, CREATE_sJ(o, j, k));
|
||||
@@ -436,9 +446,9 @@ static int codesJ (FuncState *fs, OpCode o, int sj, int k) {
|
||||
/*
|
||||
** Emit an "extra argument" instruction (format 'iAx')
|
||||
*/
|
||||
static int codeextraarg (FuncState *fs, int a) {
|
||||
lua_assert(a <= MAXARG_Ax);
|
||||
return luaK_code(fs, CREATE_Ax(OP_EXTRAARG, a));
|
||||
static int codeextraarg (FuncState *fs, int A) {
|
||||
lua_assert(A <= MAXARG_Ax);
|
||||
return luaK_code(fs, CREATE_Ax(OP_EXTRAARG, A));
|
||||
}
|
||||
|
||||
|
||||
@@ -465,9 +475,7 @@ static int luaK_codek (FuncState *fs, int reg, int k) {
|
||||
void luaK_checkstack (FuncState *fs, int n) {
|
||||
int newstack = fs->freereg + n;
|
||||
if (newstack > fs->f->maxstacksize) {
|
||||
if (newstack >= MAXREGS)
|
||||
luaX_syntaxerror(fs->ls,
|
||||
"function or expression needs too many registers");
|
||||
luaY_checklimit(fs, newstack, MAX_FSTACK, "registers");
|
||||
fs->f->maxstacksize = cast_byte(newstack);
|
||||
}
|
||||
}
|
||||
@@ -478,7 +486,7 @@ void luaK_checkstack (FuncState *fs, int n) {
|
||||
*/
|
||||
void luaK_reserveregs (FuncState *fs, int n) {
|
||||
luaK_checkstack(fs, n);
|
||||
fs->freereg += n;
|
||||
fs->freereg = cast_byte(fs->freereg + n);
|
||||
}
|
||||
|
||||
|
||||
@@ -532,38 +540,44 @@ static void freeexps (FuncState *fs, expdesc *e1, expdesc *e2) {
|
||||
|
||||
/*
|
||||
** Add constant 'v' to prototype's list of constants (field 'k').
|
||||
*/
|
||||
static int addk (FuncState *fs, Proto *f, TValue *v) {
|
||||
lua_State *L = fs->ls->L;
|
||||
int oldsize = f->sizek;
|
||||
int k = fs->nk;
|
||||
luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants");
|
||||
while (oldsize < f->sizek)
|
||||
setnilvalue(&f->k[oldsize++]);
|
||||
setobj(L, &f->k[k], v);
|
||||
fs->nk++;
|
||||
luaC_barrier(L, f, v);
|
||||
return k;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Use scanner's table to cache position of constants in constant list
|
||||
** and try to reuse constants. Because some values should not be used
|
||||
** as keys (nil cannot be a key, integer keys can collapse with float
|
||||
** keys), the caller must provide a useful 'key' for indexing the cache.
|
||||
** Note that all functions share the same table, so entering or exiting
|
||||
** a function can make some indices wrong.
|
||||
*/
|
||||
static int addk (FuncState *fs, TValue *key, TValue *v) {
|
||||
static int k2proto (FuncState *fs, TValue *key, TValue *v) {
|
||||
TValue val;
|
||||
lua_State *L = fs->ls->L;
|
||||
Proto *f = fs->f;
|
||||
const TValue *idx = luaH_get(fs->ls->h, key); /* query scanner table */
|
||||
int k, oldsize;
|
||||
if (ttisinteger(idx)) { /* is there an index there? */
|
||||
k = cast_int(ivalue(idx));
|
||||
/* correct value? (warning: must distinguish floats from integers!) */
|
||||
if (k < fs->nk && ttypetag(&f->k[k]) == ttypetag(v) &&
|
||||
luaV_rawequalobj(&f->k[k], v))
|
||||
return k; /* reuse index */
|
||||
int tag = luaH_get(fs->kcache, key, &val); /* query scanner table */
|
||||
int k;
|
||||
if (!tagisempty(tag)) { /* is there an index there? */
|
||||
k = cast_int(ivalue(&val));
|
||||
/* collisions can happen only for float keys */
|
||||
lua_assert(ttisfloat(key) || luaV_rawequalobj(&f->k[k], v));
|
||||
return k; /* reuse index */
|
||||
}
|
||||
/* constant not found; create a new entry */
|
||||
oldsize = f->sizek;
|
||||
k = fs->nk;
|
||||
/* numerical value does not need GC barrier;
|
||||
table has no metatable, so it does not need to invalidate cache */
|
||||
k = addk(fs, f, v);
|
||||
/* cache it for reuse; numerical value does not need GC barrier;
|
||||
table is not a metatable, so it does not need to invalidate cache */
|
||||
setivalue(&val, k);
|
||||
luaH_finishset(L, fs->ls->h, key, idx, &val);
|
||||
luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants");
|
||||
while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
|
||||
setobj(L, &f->k[k], v);
|
||||
fs->nk++;
|
||||
luaC_barrier(L, f, v);
|
||||
luaH_set(fs->ls->L, fs->kcache, key, &val);
|
||||
return k;
|
||||
}
|
||||
|
||||
@@ -574,30 +588,52 @@ static int addk (FuncState *fs, TValue *key, TValue *v) {
|
||||
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 */
|
||||
return k2proto(fs, &o, &o); /* use string itself as key */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Add an integer to list of constants and return its index.
|
||||
** Integers use userdata as keys to avoid collision with floats with
|
||||
** same value; conversion to 'void*' is used only for hashing, so there
|
||||
** are no "precision" problems.
|
||||
*/
|
||||
static int luaK_intK (FuncState *fs, lua_Integer n) {
|
||||
TValue k, o;
|
||||
setpvalue(&k, cast_voidp(cast_sizet(n)));
|
||||
TValue o;
|
||||
setivalue(&o, n);
|
||||
return addk(fs, &k, &o);
|
||||
return k2proto(fs, &o, &o); /* use integer itself as key */
|
||||
}
|
||||
|
||||
/*
|
||||
** Add a float to list of constants and return its index.
|
||||
** Add a float to list of constants and return its index. Floats
|
||||
** with integral values need a different key, to avoid collision
|
||||
** with actual integers. To that, we add to the number its smaller
|
||||
** power-of-two fraction that is still significant in its scale.
|
||||
** For doubles, that would be 1/2^52.
|
||||
** This method is not bulletproof: different numbers may generate the
|
||||
** same key (e.g., very large numbers will overflow to 'inf') and for
|
||||
** floats larger than 2^53 the result is still an integer. At worst,
|
||||
** this only wastes an entry with a duplicate.
|
||||
*/
|
||||
static int luaK_numberK (FuncState *fs, lua_Number r) {
|
||||
TValue o;
|
||||
setfltvalue(&o, r);
|
||||
return addk(fs, &o, &o); /* use number itself as key */
|
||||
TValue o, kv;
|
||||
setfltvalue(&o, r); /* value as a TValue */
|
||||
if (r == 0) { /* handle zero as a special case */
|
||||
setpvalue(&kv, fs); /* use FuncState as index */
|
||||
return k2proto(fs, &kv, &o); /* cannot collide */
|
||||
}
|
||||
else {
|
||||
const int nbm = l_floatatt(MANT_DIG);
|
||||
const lua_Number q = l_mathop(ldexp)(l_mathop(1.0), -nbm + 1);
|
||||
const lua_Number k = r * (1 + q); /* key */
|
||||
lua_Integer ik;
|
||||
setfltvalue(&kv, k); /* key as a TValue */
|
||||
if (!luaV_flttointeger(k, &ik, F2Ieq)) { /* not an integral value? */
|
||||
int n = k2proto(fs, &kv, &o); /* use key */
|
||||
if (luaV_rawequalobj(&fs->f->k[n], &o)) /* correct value? */
|
||||
return n;
|
||||
}
|
||||
/* else, either key is still an integer or there was a collision;
|
||||
anyway, do not try to reuse constant; instead, create a new one */
|
||||
return addk(fs, fs->f, &o);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -607,7 +643,7 @@ static int luaK_numberK (FuncState *fs, lua_Number r) {
|
||||
static int boolF (FuncState *fs) {
|
||||
TValue o;
|
||||
setbfvalue(&o);
|
||||
return addk(fs, &o, &o); /* use boolean itself as key */
|
||||
return k2proto(fs, &o, &o); /* use boolean itself as key */
|
||||
}
|
||||
|
||||
|
||||
@@ -617,7 +653,7 @@ static int boolF (FuncState *fs) {
|
||||
static int boolT (FuncState *fs) {
|
||||
TValue o;
|
||||
setbtvalue(&o);
|
||||
return addk(fs, &o, &o); /* use boolean itself as key */
|
||||
return k2proto(fs, &o, &o); /* use boolean itself as key */
|
||||
}
|
||||
|
||||
|
||||
@@ -627,9 +663,9 @@ static int boolT (FuncState *fs) {
|
||||
static int nilK (FuncState *fs) {
|
||||
TValue k, v;
|
||||
setnilvalue(&v);
|
||||
/* cannot use nil as key; instead use table itself to represent nil */
|
||||
sethvalue(fs->ls->L, &k, fs->ls->h);
|
||||
return addk(fs, &k, &v);
|
||||
/* cannot use nil as key; instead use table itself */
|
||||
sethvalue(fs->ls->L, &k, fs->kcache);
|
||||
return k2proto(fs, &k, &v);
|
||||
}
|
||||
|
||||
|
||||
@@ -653,7 +689,7 @@ static int fitsBx (lua_Integer i) {
|
||||
|
||||
void luaK_int (FuncState *fs, int reg, lua_Integer i) {
|
||||
if (fitsBx(i))
|
||||
luaK_codeAsBx(fs, OP_LOADI, reg, cast_int(i));
|
||||
codeAsBx(fs, OP_LOADI, reg, cast_int(i));
|
||||
else
|
||||
luaK_codek(fs, reg, luaK_intK(fs, i));
|
||||
}
|
||||
@@ -662,7 +698,7 @@ void luaK_int (FuncState *fs, int reg, lua_Integer i) {
|
||||
static void luaK_float (FuncState *fs, int reg, lua_Number f) {
|
||||
lua_Integer fi;
|
||||
if (luaV_flttointeger(f, &fi, F2Ieq) && fitsBx(fi))
|
||||
luaK_codeAsBx(fs, OP_LOADF, reg, cast_int(fi));
|
||||
codeAsBx(fs, OP_LOADF, reg, cast_int(fi));
|
||||
else
|
||||
luaK_codek(fs, reg, luaK_numberK(fs, f));
|
||||
}
|
||||
@@ -702,6 +738,7 @@ static void const2exp (TValue *v, expdesc *e) {
|
||||
*/
|
||||
void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) {
|
||||
Instruction *pc = &getinstruction(fs, e);
|
||||
luaY_checklimit(fs, nresults + 1, MAXARG_C, "multiple results");
|
||||
if (e->k == VCALL) /* expression is an open function call? */
|
||||
SETARG_C(*pc, nresults + 1);
|
||||
else {
|
||||
@@ -716,10 +753,11 @@ void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) {
|
||||
/*
|
||||
** Convert a VKSTR to a VK
|
||||
*/
|
||||
static void str2K (FuncState *fs, expdesc *e) {
|
||||
static int str2K (FuncState *fs, expdesc *e) {
|
||||
lua_assert(e->k == VKSTR);
|
||||
e->u.info = stringK(fs, e->u.strval);
|
||||
e->k = VK;
|
||||
return e->u.info;
|
||||
}
|
||||
|
||||
|
||||
@@ -758,7 +796,8 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
|
||||
break;
|
||||
}
|
||||
case VLOCAL: { /* already in a register */
|
||||
e->u.info = e->u.var.ridx;
|
||||
int temp = e->u.var.ridx;
|
||||
e->u.info = temp; /* (can't do a direct assignment; values overlap) */
|
||||
e->k = VNONRELOC; /* becomes a non-relocatable value */
|
||||
break;
|
||||
}
|
||||
@@ -966,7 +1005,7 @@ void luaK_exp2anyregup (FuncState *fs, expdesc *e) {
|
||||
** or it is a constant.
|
||||
*/
|
||||
void luaK_exp2val (FuncState *fs, expdesc *e) {
|
||||
if (hasjumps(e))
|
||||
if (e->k == VJMP || hasjumps(e))
|
||||
luaK_exp2anyreg(fs, e);
|
||||
else
|
||||
luaK_dischargevars(fs, e);
|
||||
@@ -1007,7 +1046,7 @@ static int luaK_exp2K (FuncState *fs, expdesc *e) {
|
||||
** in the range of R/K indices).
|
||||
** Returns 1 iff expression is K.
|
||||
*/
|
||||
int luaK_exp2RK (FuncState *fs, expdesc *e) {
|
||||
static int exp2RK (FuncState *fs, expdesc *e) {
|
||||
if (luaK_exp2K(fs, e))
|
||||
return 1;
|
||||
else { /* not a constant in the right range: put it in a register */
|
||||
@@ -1017,10 +1056,10 @@ int luaK_exp2RK (FuncState *fs, expdesc *e) {
|
||||
}
|
||||
|
||||
|
||||
static void codeABRK (FuncState *fs, OpCode o, int a, int b,
|
||||
static void codeABRK (FuncState *fs, OpCode o, int A, int B,
|
||||
expdesc *ec) {
|
||||
int k = luaK_exp2RK(fs, ec);
|
||||
luaK_codeABCk(fs, o, a, b, ec->u.info, k);
|
||||
int k = exp2RK(fs, ec);
|
||||
luaK_codeABCk(fs, o, A, B, ec->u.info, k);
|
||||
}
|
||||
|
||||
|
||||
@@ -1061,22 +1100,6 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Emit SELF instruction (convert expression 'e' into 'e:key(e,').
|
||||
*/
|
||||
void luaK_self (FuncState *fs, expdesc *e, expdesc *key) {
|
||||
int ereg;
|
||||
luaK_exp2anyreg(fs, e);
|
||||
ereg = e->u.info; /* register where 'e' was placed */
|
||||
freeexp(fs, e);
|
||||
e->u.info = fs->freereg; /* base register for op_self */
|
||||
e->k = VNONRELOC; /* self expression has a fixed register */
|
||||
luaK_reserveregs(fs, 2); /* function and 'self' produced by op_self */
|
||||
codeABRK(fs, OP_SELF, e->u.info, ereg, key);
|
||||
freeexp(fs, key);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Negate condition 'e' (where 'e' is a comparison).
|
||||
*/
|
||||
@@ -1197,7 +1220,7 @@ static void codenot (FuncState *fs, expdesc *e) {
|
||||
|
||||
|
||||
/*
|
||||
** Check whether expression 'e' is a small literal string
|
||||
** Check whether expression 'e' is a short literal string
|
||||
*/
|
||||
static int isKstr (FuncState *fs, expdesc *e) {
|
||||
return (e->k == VK && !hasjumps(e) && e->u.info <= MAXARG_B &&
|
||||
@@ -1207,7 +1230,7 @@ static int isKstr (FuncState *fs, expdesc *e) {
|
||||
/*
|
||||
** Check whether expression 'e' is a literal integer.
|
||||
*/
|
||||
int luaK_isKint (expdesc *e) {
|
||||
static int isKint (expdesc *e) {
|
||||
return (e->k == VKINT && !hasjumps(e));
|
||||
}
|
||||
|
||||
@@ -1217,7 +1240,7 @@ int luaK_isKint (expdesc *e) {
|
||||
** proper range to fit in register C
|
||||
*/
|
||||
static int isCint (expdesc *e) {
|
||||
return luaK_isKint(e) && (l_castS2U(e->u.ival) <= l_castS2U(MAXARG_C));
|
||||
return isKint(e) && (l_castS2U(e->u.ival) <= l_castS2U(MAXARG_C));
|
||||
}
|
||||
|
||||
|
||||
@@ -1226,7 +1249,7 @@ static int isCint (expdesc *e) {
|
||||
** proper range to fit in register sC
|
||||
*/
|
||||
static int isSCint (expdesc *e) {
|
||||
return luaK_isKint(e) && fitsC(e->u.ival);
|
||||
return isKint(e) && fitsC(e->u.ival);
|
||||
}
|
||||
|
||||
|
||||
@@ -1251,6 +1274,33 @@ static int isSCnumber (expdesc *e, int *pi, int *isfloat) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Emit SELF instruction or equivalent: the code will convert
|
||||
** expression 'e' into 'e.key(e,'.
|
||||
*/
|
||||
void luaK_self (FuncState *fs, expdesc *e, expdesc *key) {
|
||||
int ereg, base;
|
||||
luaK_exp2anyreg(fs, e);
|
||||
ereg = e->u.info; /* register where 'e' (the receiver) was placed */
|
||||
freeexp(fs, e);
|
||||
base = e->u.info = fs->freereg; /* base register for op_self */
|
||||
e->k = VNONRELOC; /* self expression has a fixed register */
|
||||
luaK_reserveregs(fs, 2); /* method and 'self' produced by op_self */
|
||||
lua_assert(key->k == VKSTR);
|
||||
/* is method name a short string in a valid K index? */
|
||||
if (strisshr(key->u.strval) && luaK_exp2K(fs, key)) {
|
||||
/* can use 'self' opcode */
|
||||
luaK_codeABCk(fs, OP_SELF, base, ereg, key->u.info, 0);
|
||||
}
|
||||
else { /* cannot use 'self' opcode; use move+gettable */
|
||||
luaK_exp2anyreg(fs, key); /* put method name in a register */
|
||||
luaK_codeABC(fs, OP_MOVE, base + 1, ereg, 0); /* copy self to base+1 */
|
||||
luaK_codeABC(fs, OP_GETTABLE, base, ereg, key->u.info); /* get method */
|
||||
}
|
||||
freeexp(fs, key);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Create expression 't[k]'. 't' must have its final result already in a
|
||||
** register or upvalue. Upvalues can only be indexed by literal strings.
|
||||
@@ -1258,33 +1308,38 @@ static int isSCnumber (expdesc *e, int *pi, int *isfloat) {
|
||||
** values in registers.
|
||||
*/
|
||||
void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
|
||||
int keystr = -1;
|
||||
if (k->k == VKSTR)
|
||||
str2K(fs, k);
|
||||
keystr = 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 */
|
||||
if (t->k == VUPVAL) {
|
||||
t->u.ind.t = t->u.info; /* upvalue index */
|
||||
t->u.ind.idx = k->u.info; /* literal string */
|
||||
lu_byte temp = cast_byte(t->u.info); /* upvalue index */
|
||||
t->u.ind.t = temp; /* (can't do a direct assignment; values overlap) */
|
||||
lua_assert(isKstr(fs, k));
|
||||
t->u.ind.idx = cast_short(k->u.info); /* literal short string */
|
||||
t->k = VINDEXUP;
|
||||
}
|
||||
else {
|
||||
/* register index of the table */
|
||||
t->u.ind.t = (t->k == VLOCAL) ? t->u.var.ridx: t->u.info;
|
||||
t->u.ind.t = cast_byte((t->k == VLOCAL) ? t->u.var.ridx: t->u.info);
|
||||
if (isKstr(fs, k)) {
|
||||
t->u.ind.idx = k->u.info; /* literal string */
|
||||
t->u.ind.idx = cast_short(k->u.info); /* literal short string */
|
||||
t->k = VINDEXSTR;
|
||||
}
|
||||
else if (isCint(k)) {
|
||||
t->u.ind.idx = cast_int(k->u.ival); /* int. constant in proper range */
|
||||
else if (isCint(k)) { /* int. constant in proper range? */
|
||||
t->u.ind.idx = cast_short(k->u.ival);
|
||||
t->k = VINDEXI;
|
||||
}
|
||||
else {
|
||||
t->u.ind.idx = luaK_exp2anyreg(fs, k); /* register */
|
||||
t->u.ind.idx = cast_short(luaK_exp2anyreg(fs, k)); /* register */
|
||||
t->k = VINDEXED;
|
||||
}
|
||||
}
|
||||
t->u.ind.keystr = keystr; /* string index in 'k' */
|
||||
t->u.ind.ro = 0; /* by default, not read-only */
|
||||
}
|
||||
|
||||
|
||||
@@ -1333,6 +1388,35 @@ static int constfolding (FuncState *fs, int op, expdesc *e1,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a BinOpr to an OpCode (ORDER OPR - ORDER OP)
|
||||
*/
|
||||
l_sinline OpCode binopr2op (BinOpr opr, BinOpr baser, OpCode base) {
|
||||
lua_assert(baser <= opr &&
|
||||
((baser == OPR_ADD && opr <= OPR_SHR) ||
|
||||
(baser == OPR_LT && opr <= OPR_LE)));
|
||||
return cast(OpCode, (cast_int(opr) - cast_int(baser)) + cast_int(base));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a UnOpr to an OpCode (ORDER OPR - ORDER OP)
|
||||
*/
|
||||
l_sinline OpCode unopr2op (UnOpr opr) {
|
||||
return cast(OpCode, (cast_int(opr) - cast_int(OPR_MINUS)) +
|
||||
cast_int(OP_UNM));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a BinOpr to a tag method (ORDER OPR - ORDER TM)
|
||||
*/
|
||||
l_sinline TMS binopr2TM (BinOpr opr) {
|
||||
lua_assert(OPR_ADD <= opr && opr <= OPR_SHR);
|
||||
return cast(TMS, (cast_int(opr) - cast_int(OPR_ADD)) + cast_int(TM_ADD));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Emit code for unary expressions that "produce values"
|
||||
** (everything but 'not').
|
||||
@@ -1362,7 +1446,7 @@ static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *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_codeABCk(fs, mmop, v1, v2, cast_int(event), flip); /* metamethod */
|
||||
luaK_fixline(fs, line);
|
||||
}
|
||||
|
||||
@@ -1371,12 +1455,15 @@ static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2,
|
||||
** Emit code for binary expressions that "produce values" over
|
||||
** two registers.
|
||||
*/
|
||||
static void codebinexpval (FuncState *fs, OpCode op,
|
||||
static void codebinexpval (FuncState *fs, BinOpr opr,
|
||||
expdesc *e1, expdesc *e2, int line) {
|
||||
int v2 = luaK_exp2anyreg(fs, e2); /* both operands are in registers */
|
||||
OpCode op = binopr2op(opr, OPR_ADD, OP_ADD);
|
||||
int v2 = luaK_exp2anyreg(fs, e2); /* make sure 'e2' is in a register */
|
||||
/* 'e1' must be already in a register or it is a constant */
|
||||
lua_assert((VNIL <= e1->k && e1->k <= VKSTR) ||
|
||||
e1->k == VNONRELOC || e1->k == VRELOC);
|
||||
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));
|
||||
finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN, binopr2TM(opr));
|
||||
}
|
||||
|
||||
|
||||
@@ -1392,12 +1479,24 @@ static void codebini (FuncState *fs, OpCode op,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Code binary operators with K operand.
|
||||
*/
|
||||
static void codebinK (FuncState *fs, BinOpr opr,
|
||||
expdesc *e1, expdesc *e2, int flip, int line) {
|
||||
TMS event = binopr2TM(opr);
|
||||
int v2 = e2->u.info; /* K index */
|
||||
OpCode op = binopr2op(opr, OPR_ADD, OP_ADDK);
|
||||
finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event);
|
||||
}
|
||||
|
||||
|
||||
/* Try to code a binary operator negating its second operand.
|
||||
** For the metamethod, 2nd operand must keep its original value.
|
||||
*/
|
||||
static int finishbinexpneg (FuncState *fs, expdesc *e1, expdesc *e2,
|
||||
OpCode op, int line, TMS event) {
|
||||
if (!luaK_isKint(e2))
|
||||
if (!isKint(e2))
|
||||
return 0; /* not an integer constant */
|
||||
else {
|
||||
lua_Integer i2 = e2->u.ival;
|
||||
@@ -1419,24 +1518,27 @@ static void swapexps (expdesc *e1, expdesc *e2) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Code binary operators with no constant operand.
|
||||
*/
|
||||
static void codebinNoK (FuncState *fs, BinOpr opr,
|
||||
expdesc *e1, expdesc *e2, int flip, int line) {
|
||||
if (flip)
|
||||
swapexps(e1, e2); /* back to original order */
|
||||
codebinexpval(fs, opr, e1, e2, line); /* use standard operators */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Code arithmetic operators ('+', '-', ...). If second operand is a
|
||||
** constant in the proper range, use variant opcodes with K operands.
|
||||
*/
|
||||
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 */
|
||||
}
|
||||
if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) /* K operand? */
|
||||
codebinK(fs, opr, e1, e2, flip, line);
|
||||
else /* 'e2' is neither an immediate nor a K operand */
|
||||
codebinNoK(fs, opr, e1, e2, flip, line);
|
||||
}
|
||||
|
||||
|
||||
@@ -1453,35 +1555,27 @@ static void codecommutative (FuncState *fs, BinOpr op,
|
||||
flip = 1;
|
||||
}
|
||||
if (op == OPR_ADD && isSCint(e2)) /* immediate operand? */
|
||||
codebini(fs, cast(OpCode, OP_ADDI), e1, e2, flip, line, TM_ADD);
|
||||
codebini(fs, OP_ADDI, e1, e2, flip, line, TM_ADD);
|
||||
else
|
||||
codearith(fs, op, e1, e2, flip, line);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Code bitwise operations; they are all associative, so the function
|
||||
** Code bitwise operations; they are all commutative, so the function
|
||||
** tries to put an integer constant as the 2nd operand (a K operand).
|
||||
*/
|
||||
static void codebitwise (FuncState *fs, BinOpr opr,
|
||||
expdesc *e1, expdesc *e2, int line) {
|
||||
int flip = 0;
|
||||
int v2;
|
||||
OpCode op;
|
||||
if (e1->k == VKINT && luaK_exp2RK(fs, e1)) {
|
||||
if (e1->k == VKINT) {
|
||||
swapexps(e1, e2); /* 'e2' will be the constant operand */
|
||||
flip = 1;
|
||||
}
|
||||
else if (!(e2->k == VKINT && luaK_exp2RK(fs, e2))) { /* no constants? */
|
||||
op = cast(OpCode, opr + OP_ADD);
|
||||
codebinexpval(fs, op, e1, e2, line); /* all-register opcodes */
|
||||
return;
|
||||
}
|
||||
v2 = e2->u.info; /* index in K array */
|
||||
op = cast(OpCode, opr + OP_ADDK);
|
||||
lua_assert(ttisinteger(&fs->f->k[v2]));
|
||||
finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK,
|
||||
cast(TMS, opr + TM_ADD));
|
||||
if (e2->k == VKINT && luaK_exp2K(fs, e2)) /* K operand? */
|
||||
codebinK(fs, opr, e1, e2, flip, line);
|
||||
else /* no constants */
|
||||
codebinNoK(fs, opr, e1, e2, flip, line);
|
||||
}
|
||||
|
||||
|
||||
@@ -1489,25 +1583,27 @@ static void codebitwise (FuncState *fs, BinOpr opr,
|
||||
** 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) {
|
||||
static void codeorder (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
|
||||
int r1, r2;
|
||||
int im;
|
||||
int isfloat = 0;
|
||||
OpCode op;
|
||||
if (isSCnumber(e2, &im, &isfloat)) {
|
||||
/* use immediate operand */
|
||||
r1 = luaK_exp2anyreg(fs, e1);
|
||||
r2 = im;
|
||||
op = cast(OpCode, (op - OP_LT) + OP_LTI);
|
||||
op = binopr2op(opr, OPR_LT, OP_LTI);
|
||||
}
|
||||
else if (isSCnumber(e1, &im, &isfloat)) {
|
||||
/* transform (A < B) to (B > A) and (A <= B) to (B >= A) */
|
||||
r1 = luaK_exp2anyreg(fs, e2);
|
||||
r2 = im;
|
||||
op = (op == OP_LT) ? OP_GTI : OP_GEI;
|
||||
op = binopr2op(opr, OPR_LT, OP_GTI);
|
||||
}
|
||||
else { /* regular case, compare two registers */
|
||||
r1 = luaK_exp2anyreg(fs, e1);
|
||||
r2 = luaK_exp2anyreg(fs, e2);
|
||||
op = binopr2op(opr, OPR_LT, OP_LT);
|
||||
}
|
||||
freeexps(fs, e1, e2);
|
||||
e1->u.info = condjump(fs, op, r1, r2, isfloat, 1);
|
||||
@@ -1533,7 +1629,7 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
|
||||
op = OP_EQI;
|
||||
r2 = im; /* immediate operand */
|
||||
}
|
||||
else if (luaK_exp2RK(fs, e2)) { /* 1st expression is constant? */
|
||||
else if (exp2RK(fs, e2)) { /* 2nd expression is constant? */
|
||||
op = OP_EQK;
|
||||
r2 = e2->u.info; /* constant index */
|
||||
}
|
||||
@@ -1550,16 +1646,16 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
|
||||
/*
|
||||
** Apply prefix operation 'op' to expression 'e'.
|
||||
*/
|
||||
void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) {
|
||||
void luaK_prefix (FuncState *fs, UnOpr opr, expdesc *e, int line) {
|
||||
static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP};
|
||||
luaK_dischargevars(fs, e);
|
||||
switch (op) {
|
||||
switch (opr) {
|
||||
case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */
|
||||
if (constfolding(fs, op + LUA_OPUNM, e, &ef))
|
||||
if (constfolding(fs, cast_int(opr + LUA_OPUNM), e, &ef))
|
||||
break;
|
||||
/* else */ /* FALLTHROUGH */
|
||||
case OPR_LEN:
|
||||
codeunexpval(fs, cast(OpCode, op + OP_UNM), e, line);
|
||||
codeunexpval(fs, unopr2op(opr), e, line);
|
||||
break;
|
||||
case OPR_NOT: codenot(fs, e); break;
|
||||
default: lua_assert(0);
|
||||
@@ -1593,12 +1689,13 @@ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
|
||||
case OPR_SHL: case OPR_SHR: {
|
||||
if (!tonumeral(v, NULL))
|
||||
luaK_exp2anyreg(fs, v);
|
||||
/* else keep numeral, which may be folded with 2nd operand */
|
||||
/* else keep numeral, which may be folded or used as an immediate
|
||||
operand */
|
||||
break;
|
||||
}
|
||||
case OPR_EQ: case OPR_NE: {
|
||||
if (!tonumeral(v, NULL))
|
||||
luaK_exp2RK(fs, v);
|
||||
exp2RK(fs, v);
|
||||
/* else keep numeral, which may be an immediate operand */
|
||||
break;
|
||||
}
|
||||
@@ -1642,7 +1739,7 @@ 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))
|
||||
if (foldbinop(opr) && constfolding(fs, cast_int(opr + LUA_OPADD), e1, e2))
|
||||
return; /* done by folding */
|
||||
switch (opr) {
|
||||
case OPR_AND: {
|
||||
@@ -1688,30 +1785,27 @@ void luaK_posfix (FuncState *fs, BinOpr opr,
|
||||
/* coded as (r1 >> -I) */;
|
||||
}
|
||||
else /* regular case (two registers) */
|
||||
codebinexpval(fs, OP_SHL, e1, e2, line);
|
||||
codebinexpval(fs, opr, e1, e2, line);
|
||||
break;
|
||||
}
|
||||
case OPR_SHR: {
|
||||
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);
|
||||
codebinexpval(fs, opr, e1, e2, line);
|
||||
break;
|
||||
}
|
||||
case OPR_EQ: case OPR_NE: {
|
||||
codeeq(fs, opr, e1, e2);
|
||||
break;
|
||||
}
|
||||
case OPR_LT: case OPR_LE: {
|
||||
OpCode op = cast(OpCode, (opr - OPR_EQ) + OP_EQ);
|
||||
codeorder(fs, op, e1, e2);
|
||||
break;
|
||||
}
|
||||
case OPR_GT: case OPR_GE: {
|
||||
/* '(a > b)' <=> '(b < a)'; '(a >= b)' <=> '(b <= a)' */
|
||||
OpCode op = cast(OpCode, (opr - OPR_NE) + OP_EQ);
|
||||
swapexps(e1, e2);
|
||||
codeorder(fs, op, e1, e2);
|
||||
opr = cast(BinOpr, (opr - OPR_GT) + OPR_LT);
|
||||
} /* FALLTHROUGH */
|
||||
case OPR_LT: case OPR_LE: {
|
||||
codeorder(fs, opr, e1, e2);
|
||||
break;
|
||||
}
|
||||
default: lua_assert(0);
|
||||
@@ -1731,11 +1825,11 @@ void luaK_fixline (FuncState *fs, int line) {
|
||||
|
||||
void luaK_settablesize (FuncState *fs, int pc, int ra, int asize, int hsize) {
|
||||
Instruction *inst = &fs->f->code[pc];
|
||||
int rb = (hsize != 0) ? luaO_ceillog2(hsize) + 1 : 0; /* hash size */
|
||||
int extra = asize / (MAXARG_C + 1); /* higher bits of array size */
|
||||
int rc = asize % (MAXARG_C + 1); /* lower bits of array size */
|
||||
int extra = asize / (MAXARG_vC + 1); /* higher bits of array size */
|
||||
int rc = asize % (MAXARG_vC + 1); /* lower bits of array size */
|
||||
int k = (extra > 0); /* true iff needs extra argument */
|
||||
*inst = CREATE_ABCk(OP_NEWTABLE, ra, rb, rc, k);
|
||||
hsize = (hsize != 0) ? luaO_ceillog2(cast_uint(hsize)) + 1 : 0;
|
||||
*inst = CREATE_vABCk(OP_NEWTABLE, ra, hsize, rc, k);
|
||||
*(inst + 1) = CREATE_Ax(OP_EXTRAARG, extra);
|
||||
}
|
||||
|
||||
@@ -1748,18 +1842,18 @@ void luaK_settablesize (FuncState *fs, int pc, int ra, int asize, int hsize) {
|
||||
** table (or LUA_MULTRET to add up to stack top).
|
||||
*/
|
||||
void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) {
|
||||
lua_assert(tostore != 0 && tostore <= LFIELDS_PER_FLUSH);
|
||||
lua_assert(tostore != 0);
|
||||
if (tostore == LUA_MULTRET)
|
||||
tostore = 0;
|
||||
if (nelems <= MAXARG_C)
|
||||
luaK_codeABC(fs, OP_SETLIST, base, tostore, nelems);
|
||||
if (nelems <= MAXARG_vC)
|
||||
luaK_codevABCk(fs, OP_SETLIST, base, tostore, nelems, 0);
|
||||
else {
|
||||
int extra = nelems / (MAXARG_C + 1);
|
||||
nelems %= (MAXARG_C + 1);
|
||||
luaK_codeABCk(fs, OP_SETLIST, base, tostore, nelems, 1);
|
||||
int extra = nelems / (MAXARG_vC + 1);
|
||||
nelems %= (MAXARG_vC + 1);
|
||||
luaK_codevABCk(fs, OP_SETLIST, base, tostore, nelems, 1);
|
||||
codeextraarg(fs, extra);
|
||||
}
|
||||
fs->freereg = base + 1; /* free registers with list values */
|
||||
fs->freereg = cast_byte(base + 1); /* free registers with list values */
|
||||
}
|
||||
|
||||
|
||||
@@ -1772,8 +1866,8 @@ static int finaltarget (Instruction *code, int i) {
|
||||
Instruction pc = code[i];
|
||||
if (GET_OPCODE(pc) != OP_JMP)
|
||||
break;
|
||||
else
|
||||
i += GETARG_sJ(pc) + 1;
|
||||
else
|
||||
i += GETARG_sJ(pc) + 1;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
@@ -1783,15 +1877,18 @@ static int finaltarget (Instruction *code, int i) {
|
||||
** Do a final pass over the code of a function, doing small peephole
|
||||
** optimizations and adjustments.
|
||||
*/
|
||||
#include "lopnames.h"
|
||||
void luaK_finish (FuncState *fs) {
|
||||
int i;
|
||||
Proto *p = fs->f;
|
||||
for (i = 0; i < fs->pc; i++) {
|
||||
Instruction *pc = &p->code[i];
|
||||
lua_assert(i == 0 || isOT(*(pc - 1)) == isIT(*pc));
|
||||
/* avoid "not used" warnings when assert is off (for 'onelua.c') */
|
||||
(void)luaP_isOT; (void)luaP_isIT;
|
||||
lua_assert(i == 0 || luaP_isOT(*(pc - 1)) == luaP_isIT(*pc));
|
||||
switch (GET_OPCODE(*pc)) {
|
||||
case OP_RETURN0: case OP_RETURN1: {
|
||||
if (!(fs->needclose || p->is_vararg))
|
||||
if (!(fs->needclose || (p->flag & PF_ISVARARG)))
|
||||
break; /* no extra work */
|
||||
/* else use OP_RETURN to do the extra work */
|
||||
SET_OPCODE(*pc, OP_RETURN);
|
||||
@@ -1799,7 +1896,7 @@ void luaK_finish (FuncState *fs) {
|
||||
case OP_RETURN: case OP_TAILCALL: {
|
||||
if (fs->needclose)
|
||||
SETARG_k(*pc, 1); /* signal that it needs to close */
|
||||
if (p->is_vararg)
|
||||
if (p->flag & PF_ISVARARG)
|
||||
SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */
|
||||
break;
|
||||
}
|
||||
|
||||
13
lcode.h
13
lcode.h
@@ -60,11 +60,11 @@ 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_codeABx (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_codevABCk (FuncState *fs, OpCode o, int A, int B, int C,
|
||||
int k);
|
||||
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);
|
||||
@@ -76,7 +76,6 @@ LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key);
|
||||
LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k);
|
||||
LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e);
|
||||
@@ -98,7 +97,7 @@ LUAI_FUNC void luaK_settablesize (FuncState *fs, int pc,
|
||||
int ra, int asize, int hsize);
|
||||
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);
|
||||
LUAI_FUNC l_noret luaK_semerror (LexState *ls, const char *fmt, ...);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
25
lcorolib.c
25
lcorolib.c
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
#include "llimits.h"
|
||||
|
||||
|
||||
static lua_State *getco (lua_State *L) {
|
||||
@@ -76,9 +77,9 @@ static int luaB_auxwrap (lua_State *L) {
|
||||
if (l_unlikely(r < 0)) { /* error? */
|
||||
int stat = lua_status(co);
|
||||
if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */
|
||||
stat = lua_resetthread(co); /* close its tbc variables */
|
||||
stat = lua_closethread(co, L); /* close its tbc variables */
|
||||
lua_assert(stat != LUA_OK);
|
||||
lua_xmove(co, L, 1); /* copy error message */
|
||||
lua_xmove(co, L, 1); /* move error message to the caller */
|
||||
}
|
||||
if (stat != LUA_ERRMEM && /* not a memory error and ... */
|
||||
lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */
|
||||
@@ -153,8 +154,13 @@ static int luaB_costatus (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
static lua_State *getoptco (lua_State *L) {
|
||||
return (lua_isnone(L, 1) ? L : getco(L));
|
||||
}
|
||||
|
||||
|
||||
static int luaB_yieldable (lua_State *L) {
|
||||
lua_State *co = lua_isnone(L, 1) ? L : getco(L);
|
||||
lua_State *co = getoptco(L);
|
||||
lua_pushboolean(L, lua_isyieldable(co));
|
||||
return 1;
|
||||
}
|
||||
@@ -168,21 +174,28 @@ static int luaB_corunning (lua_State *L) {
|
||||
|
||||
|
||||
static int luaB_close (lua_State *L) {
|
||||
lua_State *co = getco(L);
|
||||
lua_State *co = getoptco(L);
|
||||
int status = auxstatus(L, co);
|
||||
switch (status) {
|
||||
case COS_DEAD: case COS_YIELD: {
|
||||
status = lua_resetthread(co);
|
||||
status = lua_closethread(co, L);
|
||||
if (status == LUA_OK) {
|
||||
lua_pushboolean(L, 1);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
lua_pushboolean(L, 0);
|
||||
lua_xmove(co, L, 1); /* copy error message */
|
||||
lua_xmove(co, L, 1); /* move error message */
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
case COS_RUN: /* running coroutine? */
|
||||
lua_geti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD); /* get main */
|
||||
if (lua_tothread(L, -1) == co)
|
||||
return luaL_error(L, "cannot close main thread");
|
||||
lua_closethread(co, L); /* close itself */
|
||||
lua_assert(0); /* previous call does not return */
|
||||
return 0;
|
||||
default: /* normal or running coroutine */
|
||||
return luaL_error(L, "cannot close a %s coroutine", statname[status]);
|
||||
}
|
||||
|
||||
14
ldblib.c
14
ldblib.c
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
#include "llimits.h"
|
||||
|
||||
|
||||
/*
|
||||
@@ -190,8 +191,10 @@ static int db_getinfo (lua_State *L) {
|
||||
settabsi(L, "ftransfer", ar.ftransfer);
|
||||
settabsi(L, "ntransfer", ar.ntransfer);
|
||||
}
|
||||
if (strchr(options, 't'))
|
||||
if (strchr(options, 't')) {
|
||||
settabsb(L, "istailcall", ar.istailcall);
|
||||
settabsi(L, "extraargs", ar.extraargs);
|
||||
}
|
||||
if (strchr(options, 'L'))
|
||||
treatstackoption(L, L1, "activelines");
|
||||
if (strchr(options, 'f'))
|
||||
@@ -446,14 +449,6 @@ 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);
|
||||
lua_pushinteger(L, res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static const luaL_Reg dblib[] = {
|
||||
{"debug", db_debug},
|
||||
{"getuservalue", db_getuservalue},
|
||||
@@ -471,7 +466,6 @@ static const luaL_Reg dblib[] = {
|
||||
{"setmetatable", db_setmetatable},
|
||||
{"setupvalue", db_setupvalue},
|
||||
{"traceback", db_traceback},
|
||||
{"setcstacklimit", db_setcstacklimit},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
438
ldebug.c
438
ldebug.c
@@ -31,11 +31,13 @@
|
||||
|
||||
|
||||
|
||||
#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL)
|
||||
#define LuaClosure(f) ((f) != NULL && (f)->c.tt == LUA_VLCL)
|
||||
|
||||
static const char strlocal[] = "local";
|
||||
static const char strupval[] = "upvalue";
|
||||
|
||||
static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
||||
const char **name);
|
||||
static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
|
||||
const char **name);
|
||||
|
||||
|
||||
static int currentpc (CallInfo *ci) {
|
||||
@@ -63,8 +65,8 @@ static int getbaseline (const Proto *f, int pc, int *basepc) {
|
||||
return f->linedefined;
|
||||
}
|
||||
else {
|
||||
int i = cast_uint(pc) / MAXIWTHABS - 1; /* get an estimate */
|
||||
/* estimate must be a lower bond of the correct base */
|
||||
int i = pc / MAXIWTHABS - 1; /* get an estimate */
|
||||
/* estimate must be a lower bound of the correct base */
|
||||
lua_assert(i < 0 ||
|
||||
(i < f->sizeabslineinfo && f->abslineinfo[i].pc <= pc));
|
||||
while (i + 1 < f->sizeabslineinfo && pc >= f->abslineinfo[i + 1].pc)
|
||||
@@ -182,10 +184,10 @@ static const char *upvalname (const Proto *p, int uv) {
|
||||
|
||||
|
||||
static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
|
||||
if (clLvalue(s2v(ci->func))->p->is_vararg) {
|
||||
if (clLvalue(s2v(ci->func.p))->p->flag & PF_ISVARARG) {
|
||||
int nextra = ci->u.l.nextraargs;
|
||||
if (n >= -nextra) { /* 'n' is negative */
|
||||
*pos = ci->func - nextra - (n + 1);
|
||||
*pos = ci->func.p - nextra - (n + 1);
|
||||
return "(vararg)"; /* generic name for any vararg */
|
||||
}
|
||||
}
|
||||
@@ -194,7 +196,7 @@ static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
|
||||
|
||||
|
||||
const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) {
|
||||
StkId base = ci->func + 1;
|
||||
StkId base = ci->func.p + 1;
|
||||
const char *name = NULL;
|
||||
if (isLua(ci)) {
|
||||
if (n < 0) /* access to vararg values? */
|
||||
@@ -203,7 +205,7 @@ const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) {
|
||||
name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
|
||||
}
|
||||
if (name == NULL) { /* no 'standard' name? */
|
||||
StkId limit = (ci == L->ci) ? L->top : ci->next->func;
|
||||
StkId limit = (ci == L->ci) ? L->top.p : ci->next->func.p;
|
||||
if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */
|
||||
/* generic name for any valid slot */
|
||||
name = isLua(ci) ? "(temporary)" : "(C temporary)";
|
||||
@@ -221,16 +223,16 @@ LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
|
||||
const char *name;
|
||||
lua_lock(L);
|
||||
if (ar == NULL) { /* information about non-active function? */
|
||||
if (!isLfunction(s2v(L->top - 1))) /* not a Lua function? */
|
||||
if (!isLfunction(s2v(L->top.p - 1))) /* not a Lua function? */
|
||||
name = NULL;
|
||||
else /* consider live variables at function start (parameters) */
|
||||
name = luaF_getlocalname(clLvalue(s2v(L->top - 1))->p, n, 0);
|
||||
name = luaF_getlocalname(clLvalue(s2v(L->top.p - 1))->p, n, 0);
|
||||
}
|
||||
else { /* active function; get information through 'ar' */
|
||||
StkId pos = NULL; /* to avoid warnings */
|
||||
name = luaG_findlocal(L, ar->i_ci, n, &pos);
|
||||
if (name) {
|
||||
setobjs2s(L, L->top, pos);
|
||||
setobjs2s(L, L->top.p, pos);
|
||||
api_incr_top(L);
|
||||
}
|
||||
}
|
||||
@@ -245,8 +247,9 @@ LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
|
||||
lua_lock(L);
|
||||
name = luaG_findlocal(L, ar->i_ci, n, &pos);
|
||||
if (name) {
|
||||
setobjs2s(L, pos, L->top - 1);
|
||||
L->top--; /* pop value */
|
||||
api_checkpop(L, 1);
|
||||
setobjs2s(L, pos, L->top.p - 1);
|
||||
L->top.p--; /* pop value */
|
||||
}
|
||||
lua_unlock(L);
|
||||
return name;
|
||||
@@ -254,7 +257,7 @@ 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)) {
|
||||
if (!LuaClosure(cl)) {
|
||||
ar->source = "=[C]";
|
||||
ar->srclen = LL("=[C]");
|
||||
ar->linedefined = -1;
|
||||
@@ -264,8 +267,7 @@ static void funcinfo (lua_Debug *ar, Closure *cl) {
|
||||
else {
|
||||
const Proto *p = cl->l.p;
|
||||
if (p->source) {
|
||||
ar->source = getstr(p->source);
|
||||
ar->srclen = tsslen(p->source);
|
||||
ar->source = getlstr(p->source, ar->srclen);
|
||||
}
|
||||
else {
|
||||
ar->source = "=?";
|
||||
@@ -288,37 +290,40 @@ static int nextline (const Proto *p, int currentline, int pc) {
|
||||
|
||||
|
||||
static void collectvalidlines (lua_State *L, Closure *f) {
|
||||
if (noLuaClosure(f)) {
|
||||
setnilvalue(s2v(L->top));
|
||||
if (!LuaClosure(f)) {
|
||||
setnilvalue(s2v(L->top.p));
|
||||
api_incr_top(L);
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
TValue v;
|
||||
const Proto *p = f->l.p;
|
||||
int currentline = p->linedefined;
|
||||
Table *t = luaH_new(L); /* new table to store active lines */
|
||||
sethvalue2s(L, L->top, t); /* push it on stack */
|
||||
sethvalue2s(L, L->top.p, t); /* push it on stack */
|
||||
api_incr_top(L);
|
||||
setbtvalue(&v); /* boolean 'true' to be the value of all indices */
|
||||
for (i = 0; i < p->sizelineinfo; i++) { /* for all instructions */
|
||||
currentline = nextline(p, currentline, i); /* get its line */
|
||||
luaH_setint(L, t, currentline, &v); /* table[line] = true */
|
||||
if (p->lineinfo != NULL) { /* proto with debug information? */
|
||||
int i;
|
||||
TValue v;
|
||||
setbtvalue(&v); /* boolean 'true' to be the value of all indices */
|
||||
if (!(p->flag & PF_ISVARARG)) /* regular function? */
|
||||
i = 0; /* consider all instructions */
|
||||
else { /* vararg function */
|
||||
lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP);
|
||||
currentline = nextline(p, currentline, 0);
|
||||
i = 1; /* skip first instruction (OP_VARARGPREP) */
|
||||
}
|
||||
for (; i < p->sizelineinfo; i++) { /* for each instruction */
|
||||
currentline = nextline(p, currentline, i); /* get its line */
|
||||
luaH_setint(L, t, currentline, &v); /* table[line] = true */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {
|
||||
if (ci == NULL) /* no 'ci'? */
|
||||
return NULL; /* no info */
|
||||
else if (ci->callstatus & CIST_FIN) { /* is this a finalizer? */
|
||||
*name = "__gc";
|
||||
return "metamethod"; /* report it as such */
|
||||
}
|
||||
/* calling function is a known Lua function? */
|
||||
else if (!(ci->callstatus & CIST_TAIL) && isLua(ci->previous))
|
||||
return funcnamefromcode(L, ci->previous, name);
|
||||
/* calling function is a known function? */
|
||||
if (ci != NULL && !(ci->callstatus & CIST_TAIL))
|
||||
return funcnamefromcall(L, ci->previous, name);
|
||||
else return NULL; /* no way to find a name */
|
||||
}
|
||||
|
||||
@@ -338,18 +343,26 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
|
||||
}
|
||||
case 'u': {
|
||||
ar->nups = (f == NULL) ? 0 : f->c.nupvalues;
|
||||
if (noLuaClosure(f)) {
|
||||
if (!LuaClosure(f)) {
|
||||
ar->isvararg = 1;
|
||||
ar->nparams = 0;
|
||||
}
|
||||
else {
|
||||
ar->isvararg = f->l.p->is_vararg;
|
||||
ar->isvararg = (f->l.p->flag & PF_ISVARARG) ? 1 : 0;
|
||||
ar->nparams = f->l.p->numparams;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 't': {
|
||||
ar->istailcall = (ci) ? ci->callstatus & CIST_TAIL : 0;
|
||||
if (ci != NULL) {
|
||||
ar->istailcall = !!(ci->callstatus & CIST_TAIL);
|
||||
ar->extraargs =
|
||||
cast_uchar((ci->callstatus & MAX_CCMT) >> CIST_CCMT);
|
||||
}
|
||||
else {
|
||||
ar->istailcall = 0;
|
||||
ar->extraargs = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'n': {
|
||||
@@ -361,11 +374,11 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
|
||||
break;
|
||||
}
|
||||
case 'r': {
|
||||
if (ci == NULL || !(ci->callstatus & CIST_TRAN))
|
||||
if (ci == NULL || !(ci->callstatus & CIST_HOOKED))
|
||||
ar->ftransfer = ar->ntransfer = 0;
|
||||
else {
|
||||
ar->ftransfer = ci->u2.transferinfo.ftransfer;
|
||||
ar->ntransfer = ci->u2.transferinfo.ntransfer;
|
||||
ar->ftransfer = L->transferinfo.ftransfer;
|
||||
ar->ntransfer = L->transferinfo.ntransfer;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -387,20 +400,20 @@ LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
|
||||
lua_lock(L);
|
||||
if (*what == '>') {
|
||||
ci = NULL;
|
||||
func = s2v(L->top - 1);
|
||||
func = s2v(L->top.p - 1);
|
||||
api_check(L, ttisfunction(func), "function expected");
|
||||
what++; /* skip the '>' */
|
||||
L->top--; /* pop function */
|
||||
L->top.p--; /* pop function */
|
||||
}
|
||||
else {
|
||||
ci = ar->i_ci;
|
||||
func = s2v(ci->func);
|
||||
func = s2v(ci->func.p);
|
||||
lua_assert(ttisfunction(func));
|
||||
}
|
||||
cl = ttisclosure(func) ? clvalue(func) : NULL;
|
||||
status = auxgetinfo(L, what, ar, cl, ci);
|
||||
if (strchr(what, 'f')) {
|
||||
setobj2s(L, L->top, func);
|
||||
setobj2s(L, L->top.p, func);
|
||||
api_incr_top(L);
|
||||
}
|
||||
if (strchr(what, 'L'))
|
||||
@@ -416,40 +429,6 @@ LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
static const char *getobjname (const Proto *p, int lastpc, int reg,
|
||||
const char **name);
|
||||
|
||||
|
||||
/*
|
||||
** Find a "name" for the constant 'c'.
|
||||
*/
|
||||
static void kname (const Proto *p, int c, const char **name) {
|
||||
TValue *kvalue = &p->k[c];
|
||||
*name = (ttisstring(kvalue)) ? svalue(kvalue) : "?";
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Find a "name" for the register 'c'.
|
||||
*/
|
||||
static void rname (const Proto *p, int pc, int c, const char **name) {
|
||||
const char *what = getobjname(p, pc, c, name); /* search for 'c' */
|
||||
if (!(what && *what == 'c')) /* did not find a constant name? */
|
||||
*name = "?";
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Find a "name" for a 'C' value in an RK instruction.
|
||||
*/
|
||||
static void rkname (const Proto *p, int pc, Instruction i, const char **name) {
|
||||
int c = GETARG_C(i); /* key index */
|
||||
if (GETARG_k(i)) /* is 'c' a constant? */
|
||||
kname(p, c, name);
|
||||
else /* 'c' is a register */
|
||||
rname(p, pc, c, name);
|
||||
}
|
||||
|
||||
|
||||
static int filterpc (int pc, int jmptarget) {
|
||||
if (pc < jmptarget) /* is code conditional (inside a jump)? */
|
||||
@@ -508,28 +487,29 @@ static int findsetreg (const Proto *p, int lastpc, int reg) {
|
||||
|
||||
|
||||
/*
|
||||
** Check whether table being indexed by instruction 'i' is the
|
||||
** environment '_ENV'
|
||||
** Find a "name" for the constant 'c'.
|
||||
*/
|
||||
static const char *gxf (const Proto *p, int pc, Instruction i, int isup) {
|
||||
int t = GETARG_B(i); /* table index */
|
||||
const char *name; /* name of indexed variable */
|
||||
if (isup) /* is an upvalue? */
|
||||
name = upvalname(p, t);
|
||||
else
|
||||
getobjname(p, pc, t, &name);
|
||||
return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field";
|
||||
static const char *kname (const Proto *p, int index, const char **name) {
|
||||
TValue *kvalue = &p->k[index];
|
||||
if (ttisstring(kvalue)) {
|
||||
*name = getstr(tsvalue(kvalue));
|
||||
return "constant";
|
||||
}
|
||||
else {
|
||||
*name = "?";
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const char *getobjname (const Proto *p, int lastpc, int reg,
|
||||
const char **name) {
|
||||
int pc;
|
||||
*name = luaF_getlocalname(p, reg + 1, lastpc);
|
||||
static const char *basicgetobjname (const Proto *p, int *ppc, int reg,
|
||||
const char **name) {
|
||||
int pc = *ppc;
|
||||
*name = luaF_getlocalname(p, reg + 1, pc);
|
||||
if (*name) /* is a local? */
|
||||
return "local";
|
||||
return strlocal;
|
||||
/* else try symbolic execution */
|
||||
pc = findsetreg(p, lastpc, reg);
|
||||
*ppc = pc = findsetreg(p, pc, reg);
|
||||
if (pc != -1) { /* could find instruction? */
|
||||
Instruction i = p->code[pc];
|
||||
OpCode op = GET_OPCODE(i);
|
||||
@@ -537,18 +517,73 @@ static const char *getobjname (const Proto *p, int lastpc, int reg,
|
||||
case OP_MOVE: {
|
||||
int b = GETARG_B(i); /* move from 'b' to 'a' */
|
||||
if (b < GETARG_A(i))
|
||||
return getobjname(p, pc, b, name); /* get name for 'b' */
|
||||
return basicgetobjname(p, ppc, b, name); /* get name for 'b' */
|
||||
break;
|
||||
}
|
||||
case OP_GETUPVAL: {
|
||||
*name = upvalname(p, GETARG_B(i));
|
||||
return strupval;
|
||||
}
|
||||
case OP_LOADK: return kname(p, GETARG_Bx(i), name);
|
||||
case OP_LOADKX: return kname(p, GETARG_Ax(p->code[pc + 1]), name);
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
return NULL; /* could not find reasonable name */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Find a "name" for the register 'c'.
|
||||
*/
|
||||
static void rname (const Proto *p, int pc, int c, const char **name) {
|
||||
const char *what = basicgetobjname(p, &pc, c, name); /* search for 'c' */
|
||||
if (!(what && *what == 'c')) /* did not find a constant name? */
|
||||
*name = "?";
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check whether table being indexed by instruction 'i' is the
|
||||
** environment '_ENV'
|
||||
*/
|
||||
static const char *isEnv (const Proto *p, int pc, Instruction i, int isup) {
|
||||
int t = GETARG_B(i); /* table index */
|
||||
const char *name; /* name of indexed variable */
|
||||
if (isup) /* is 't' an upvalue? */
|
||||
name = upvalname(p, t);
|
||||
else { /* 't' is a register */
|
||||
const char *what = basicgetobjname(p, &pc, t, &name);
|
||||
/* 'name' must be the name of a local variable (at the current
|
||||
level or an upvalue) */
|
||||
if (what != strlocal && what != strupval)
|
||||
name = NULL; /* cannot be the variable _ENV */
|
||||
}
|
||||
return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field";
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Extend 'basicgetobjname' to handle table accesses
|
||||
*/
|
||||
static const char *getobjname (const Proto *p, int lastpc, int reg,
|
||||
const char **name) {
|
||||
const char *kind = basicgetobjname(p, &lastpc, reg, name);
|
||||
if (kind != NULL)
|
||||
return kind;
|
||||
else if (lastpc != -1) { /* could find instruction? */
|
||||
Instruction i = p->code[lastpc];
|
||||
OpCode op = GET_OPCODE(i);
|
||||
switch (op) {
|
||||
case OP_GETTABUP: {
|
||||
int k = GETARG_C(i); /* key index */
|
||||
kname(p, k, name);
|
||||
return gxf(p, pc, i, 1);
|
||||
return isEnv(p, lastpc, i, 1);
|
||||
}
|
||||
case OP_GETTABLE: {
|
||||
int k = GETARG_C(i); /* key index */
|
||||
rname(p, pc, k, name);
|
||||
return gxf(p, pc, i, 0);
|
||||
rname(p, lastpc, k, name);
|
||||
return isEnv(p, lastpc, i, 0);
|
||||
}
|
||||
case OP_GETI: {
|
||||
*name = "integer index";
|
||||
@@ -557,24 +592,11 @@ static const char *getobjname (const Proto *p, int lastpc, int reg,
|
||||
case OP_GETFIELD: {
|
||||
int k = GETARG_C(i); /* key index */
|
||||
kname(p, k, name);
|
||||
return gxf(p, pc, i, 0);
|
||||
}
|
||||
case OP_GETUPVAL: {
|
||||
*name = upvalname(p, GETARG_B(i));
|
||||
return "upvalue";
|
||||
}
|
||||
case OP_LOADK:
|
||||
case OP_LOADKX: {
|
||||
int b = (op == OP_LOADK) ? GETARG_Bx(i)
|
||||
: GETARG_Ax(p->code[pc + 1]);
|
||||
if (ttisstring(&p->k[b])) {
|
||||
*name = svalue(&p->k[b]);
|
||||
return "constant";
|
||||
}
|
||||
break;
|
||||
return isEnv(p, lastpc, i, 0);
|
||||
}
|
||||
case OP_SELF: {
|
||||
rkname(p, pc, i, name);
|
||||
int k = GETARG_C(i); /* key index */
|
||||
kname(p, k, name);
|
||||
return "method";
|
||||
}
|
||||
default: break; /* go through to return NULL */
|
||||
@@ -590,16 +612,10 @@ static const char *getobjname (const Proto *p, int lastpc, int reg,
|
||||
** Returns what the name is (e.g., "for iterator", "method",
|
||||
** "metamethod") and sets '*name' to point to the name.
|
||||
*/
|
||||
static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
||||
const char **name) {
|
||||
static const char *funcnamefromcode (lua_State *L, const Proto *p,
|
||||
int pc, const char **name) {
|
||||
TMS tm = (TMS)0; /* (initial value avoids warnings) */
|
||||
const Proto *p = ci_func(ci)->p; /* calling function */
|
||||
int pc = currentpc(ci); /* calling instruction index */
|
||||
Instruction i = p->code[pc]; /* calling instruction */
|
||||
if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */
|
||||
*name = "?";
|
||||
return "hook";
|
||||
}
|
||||
switch (GET_OPCODE(i)) {
|
||||
case OP_CALL:
|
||||
case OP_TAILCALL:
|
||||
@@ -632,27 +648,48 @@ static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
||||
default:
|
||||
return NULL; /* cannot find a reasonable name */
|
||||
}
|
||||
*name = getstr(G(L)->tmname[tm]) + 2;
|
||||
*name = getshrstr(G(L)->tmname[tm]) + 2;
|
||||
return "metamethod";
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to find a name for a function based on how it was called.
|
||||
*/
|
||||
static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
|
||||
const char **name) {
|
||||
if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */
|
||||
*name = "?";
|
||||
return "hook";
|
||||
}
|
||||
else if (ci->callstatus & CIST_FIN) { /* was it called as a finalizer? */
|
||||
*name = "__gc";
|
||||
return "metamethod"; /* report it as such */
|
||||
}
|
||||
else if (isLua(ci))
|
||||
return funcnamefromcode(L, ci_func(ci)->p, currentpc(ci), name);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Check whether pointer 'o' points to some value in the stack
|
||||
** frame of the current function. Because 'o' may not point to a
|
||||
** value in this stack, we cannot compare it with the region
|
||||
** boundaries (undefined behaviour in ISO C).
|
||||
** Check whether pointer 'o' points to some value in the stack frame of
|
||||
** the current function and, if so, returns its index. Because 'o' may
|
||||
** not point to a value in this stack, we cannot compare it with the
|
||||
** region boundaries (undefined behavior in ISO C).
|
||||
*/
|
||||
static int isinstack (CallInfo *ci, const TValue *o) {
|
||||
StkId pos;
|
||||
for (pos = ci->func + 1; pos < ci->top; pos++) {
|
||||
if (o == s2v(pos))
|
||||
return 1;
|
||||
static int instack (CallInfo *ci, const TValue *o) {
|
||||
int pos;
|
||||
StkId base = ci->func.p + 1;
|
||||
for (pos = 0; base + pos < ci->top.p; pos++) {
|
||||
if (o == s2v(base + pos))
|
||||
return pos;
|
||||
}
|
||||
return 0; /* not found */
|
||||
return -1; /* not found */
|
||||
}
|
||||
|
||||
|
||||
@@ -666,45 +703,73 @@ static const char *getupvalname (CallInfo *ci, const TValue *o,
|
||||
LClosure *c = ci_func(ci);
|
||||
int i;
|
||||
for (i = 0; i < c->nupvalues; i++) {
|
||||
if (c->upvals[i]->v == o) {
|
||||
if (c->upvals[i]->v.p == o) {
|
||||
*name = upvalname(c->p, i);
|
||||
return "upvalue";
|
||||
return strupval;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static const char *formatvarinfo (lua_State *L, const char *kind,
|
||||
const char *name) {
|
||||
if (kind == NULL)
|
||||
return ""; /* no information */
|
||||
else
|
||||
return luaO_pushfstring(L, " (%s '%s')", kind, name);
|
||||
}
|
||||
|
||||
/*
|
||||
** Build a string with a "description" for the value 'o', such as
|
||||
** "variable 'x'" or "upvalue 'y'".
|
||||
*/
|
||||
static const char *varinfo (lua_State *L, const TValue *o) {
|
||||
const char *name = NULL; /* to avoid warnings */
|
||||
CallInfo *ci = L->ci;
|
||||
const char *name = NULL; /* to avoid warnings */
|
||||
const char *kind = NULL;
|
||||
if (isLua(ci)) {
|
||||
kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */
|
||||
if (!kind && isinstack(ci, o)) /* no? try a register */
|
||||
kind = getobjname(ci_func(ci)->p, currentpc(ci),
|
||||
cast_int(cast(StkId, o) - (ci->func + 1)), &name);
|
||||
if (!kind) { /* not an upvalue? */
|
||||
int reg = instack(ci, o); /* try a register */
|
||||
if (reg >= 0) /* is 'o' a register? */
|
||||
kind = getobjname(ci_func(ci)->p, currentpc(ci), reg, &name);
|
||||
}
|
||||
}
|
||||
return (kind) ? luaO_pushfstring(L, " (%s '%s')", kind, name) : "";
|
||||
return formatvarinfo(L, kind, name);
|
||||
}
|
||||
|
||||
|
||||
l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
|
||||
/*
|
||||
** Raise a type error
|
||||
*/
|
||||
static l_noret typeerror (lua_State *L, const TValue *o, const char *op,
|
||||
const char *extra) {
|
||||
const char *t = luaT_objtypename(L, o);
|
||||
luaG_runerror(L, "attempt to %s a %s value%s", op, t, varinfo(L, o));
|
||||
luaG_runerror(L, "attempt to %s a %s value%s", op, t, extra);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Raise a type error with "standard" information about the faulty
|
||||
** object 'o' (using 'varinfo').
|
||||
*/
|
||||
l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
|
||||
typeerror(L, o, op, varinfo(L, o));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Raise an error for calling a non-callable object. Try to find a name
|
||||
** for the object based on how it was called ('funcnamefromcall'); if it
|
||||
** cannot get a name there, try 'varinfo'.
|
||||
*/
|
||||
l_noret luaG_callerror (lua_State *L, const TValue *o) {
|
||||
CallInfo *ci = L->ci;
|
||||
const char *name = NULL; /* to avoid warnings */
|
||||
const char *what = (isLua(ci)) ? funcnamefromcode(L, ci, &name) : NULL;
|
||||
if (what != NULL) {
|
||||
const char *t = luaT_objtypename(L, o);
|
||||
luaG_runerror(L, "%s '%s' is not callable (a %s value)", what, name, t);
|
||||
}
|
||||
else
|
||||
luaG_typeerror(L, o, "call");
|
||||
const char *kind = funcnamefromcall(L, ci, &name);
|
||||
const char *extra = kind ? formatvarinfo(L, kind, name) : varinfo(L, o);
|
||||
typeerror(L, o, "call", extra);
|
||||
}
|
||||
|
||||
|
||||
@@ -752,13 +817,15 @@ l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) {
|
||||
/* add src:line information to 'msg' */
|
||||
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), tsslen(src));
|
||||
else { /* no source available; use "?" instead */
|
||||
buff[0] = '?'; buff[1] = '\0';
|
||||
if (src == NULL) /* no debug information? */
|
||||
return luaO_pushfstring(L, "?:?: %s", msg);
|
||||
else {
|
||||
char buff[LUA_IDSIZE];
|
||||
size_t idlen;
|
||||
const char *id = getlstr(src, idlen);
|
||||
luaO_chunkid(buff, id, idlen);
|
||||
return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg);
|
||||
}
|
||||
return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -766,10 +833,14 @@ l_noret luaG_errormsg (lua_State *L) {
|
||||
if (L->errfunc != 0) { /* is there an error handling function? */
|
||||
StkId errfunc = restorestack(L, L->errfunc);
|
||||
lua_assert(ttisfunction(s2v(errfunc)));
|
||||
setobjs2s(L, L->top, L->top - 1); /* move argument */
|
||||
setobjs2s(L, L->top - 1, errfunc); /* push function */
|
||||
L->top++; /* assume EXTRA_STACK */
|
||||
luaD_callnoyield(L, L->top - 2, 1); /* call it */
|
||||
setobjs2s(L, L->top.p, L->top.p - 1); /* move argument */
|
||||
setobjs2s(L, L->top.p - 1, errfunc); /* push function */
|
||||
L->top.p++; /* assume EXTRA_STACK */
|
||||
luaD_callnoyield(L, L->top.p - 2, 1); /* call it */
|
||||
}
|
||||
if (ttisnil(s2v(L->top.p - 1))) { /* error object is nil? */
|
||||
/* change it to a proper message */
|
||||
setsvalue2s(L, L->top.p - 1, luaS_newliteral(L, "<no error object>"));
|
||||
}
|
||||
luaD_throw(L, LUA_ERRRUN);
|
||||
}
|
||||
@@ -780,11 +851,13 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
|
||||
const char *msg;
|
||||
va_list argp;
|
||||
luaC_checkGC(L); /* error message uses memory */
|
||||
va_start(argp, fmt);
|
||||
msg = luaO_pushvfstring(L, fmt, argp); /* format message */
|
||||
va_end(argp);
|
||||
if (isLua(ci)) /* if Lua function, add source:line information */
|
||||
pushvfstring(L, argp, fmt, msg);
|
||||
if (isLua(ci)) { /* Lua function? */
|
||||
/* add source:line information */
|
||||
luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci));
|
||||
setobjs2s(L, L->top.p - 2, L->top.p - 1); /* remove 'msg' */
|
||||
L->top.p--;
|
||||
}
|
||||
luaG_errormsg(L);
|
||||
}
|
||||
|
||||
@@ -801,7 +874,7 @@ static int changedline (const Proto *p, int oldpc, int newpc) {
|
||||
if (p->lineinfo == NULL) /* no debug information? */
|
||||
return 0;
|
||||
if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */
|
||||
int delta = 0; /* line diference */
|
||||
int delta = 0; /* line difference */
|
||||
int pc = oldpc;
|
||||
for (;;) {
|
||||
int lineinfo = p->lineinfo[++pc];
|
||||
@@ -818,6 +891,28 @@ static int changedline (const Proto *p, int oldpc, int newpc) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Traces Lua calls. If code is running the first instruction of a function,
|
||||
** and function is not vararg, and it is not coming from an yield,
|
||||
** calls 'luaD_hookcall'. (Vararg functions will call 'luaD_hookcall'
|
||||
** after adjusting its variable arguments; otherwise, they could call
|
||||
** a line/count hook before the call hook. Functions coming from
|
||||
** an yield already called 'luaD_hookcall' before yielding.)
|
||||
*/
|
||||
int luaG_tracecall (lua_State *L) {
|
||||
CallInfo *ci = L->ci;
|
||||
Proto *p = ci_func(ci)->p;
|
||||
ci->u.l.trap = 1; /* ensure hooks will be checked */
|
||||
if (ci->u.l.savedpc == p->code) { /* first instruction (not resuming)? */
|
||||
if (p->flag & PF_ISVARARG)
|
||||
return 0; /* hooks will start at VARARGPREP instruction */
|
||||
else if (!(ci->callstatus & CIST_HOOKYIELD)) /* not yielded? */
|
||||
luaD_hookcall(L, ci); /* check 'call' hook */
|
||||
}
|
||||
return 1; /* keep 'trap' on */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Traces the execution of a Lua function. Called before the execution
|
||||
** of each opcode, when debug is on. 'L->oldpc' stores the last
|
||||
@@ -828,11 +923,11 @@ static int changedline (const Proto *p, int oldpc, int newpc) {
|
||||
** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc'
|
||||
** at most causes an extra call to a line hook.)
|
||||
** This function is not "Protected" when called, so it should correct
|
||||
** 'L->top' before calling anything that can run the GC.
|
||||
** 'L->top.p' before calling anything that can run the GC.
|
||||
*/
|
||||
int luaG_traceexec (lua_State *L, const Instruction *pc) {
|
||||
CallInfo *ci = L->ci;
|
||||
lu_byte mask = L->hookmask;
|
||||
lu_byte mask = cast_byte(L->hookmask);
|
||||
const Proto *p = ci_func(ci)->p;
|
||||
int counthook;
|
||||
if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */
|
||||
@@ -841,17 +936,17 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
|
||||
}
|
||||
pc++; /* reference is always next instruction */
|
||||
ci->u.l.savedpc = pc; /* save 'pc' */
|
||||
counthook = (--L->hookcount == 0 && (mask & LUA_MASKCOUNT));
|
||||
counthook = (mask & LUA_MASKCOUNT) && (--L->hookcount == 0);
|
||||
if (counthook)
|
||||
resethookcount(L); /* reset count */
|
||||
else if (!(mask & LUA_MASKLINE))
|
||||
return 1; /* no line hook and count != 0; nothing to be done now */
|
||||
if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */
|
||||
if (ci->callstatus & CIST_HOOKYIELD) { /* hook yielded last time? */
|
||||
ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
|
||||
return 1; /* do not call hook again (VM yielded, so it did not move) */
|
||||
}
|
||||
if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */
|
||||
L->top = ci->top; /* correct top */
|
||||
if (!luaP_isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */
|
||||
L->top.p = ci->top.p; /* correct top */
|
||||
if (counthook)
|
||||
luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
|
||||
if (mask & LUA_MASKLINE) {
|
||||
@@ -868,7 +963,6 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
|
||||
if (L->status == LUA_YIELD) { /* did hook yield? */
|
||||
if (counthook)
|
||||
L->hookcount = 1; /* undo decrement to zero */
|
||||
ci->u.l.savedpc--; /* undo increment (resume will increment it again) */
|
||||
ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */
|
||||
luaD_throw(L, LUA_YIELD);
|
||||
}
|
||||
|
||||
3
ldebug.h
3
ldebug.h
@@ -15,7 +15,7 @@
|
||||
|
||||
|
||||
/* Active Lua function (given call info) */
|
||||
#define ci_func(ci) (clLvalue(s2v((ci)->func)))
|
||||
#define ci_func(ci) (clLvalue(s2v((ci)->func.p)))
|
||||
|
||||
|
||||
#define resethookcount(L) (L->hookcount = L->basehookcount)
|
||||
@@ -58,6 +58,7 @@ LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg,
|
||||
TString *src, int line);
|
||||
LUAI_FUNC l_noret luaG_errormsg (lua_State *L);
|
||||
LUAI_FUNC int luaG_traceexec (lua_State *L, const Instruction *pc);
|
||||
LUAI_FUNC int luaG_tracecall (lua_State *L);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
55
ldo.h
55
ldo.h
@@ -8,6 +8,7 @@
|
||||
#define ldo_h
|
||||
|
||||
|
||||
#include "llimits.h"
|
||||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
#include "lzio.h"
|
||||
@@ -22,49 +23,66 @@
|
||||
** 'condmovestack' is used in heavy tests to force a stack reallocation
|
||||
** at every check.
|
||||
*/
|
||||
|
||||
#if !defined(HARDSTACKTESTS)
|
||||
#define condmovestack(L,pre,pos) ((void)0)
|
||||
#else
|
||||
/* realloc stack keeping its size */
|
||||
#define condmovestack(L,pre,pos) \
|
||||
{ int sz_ = stacksize(L); pre; luaD_reallocstack((L), sz_, 0); pos; }
|
||||
#endif
|
||||
|
||||
#define luaD_checkstackaux(L,n,pre,pos) \
|
||||
if (l_unlikely(L->stack_last - L->top <= (n))) \
|
||||
if (l_unlikely(L->stack_last.p - L->top.p <= (n))) \
|
||||
{ pre; luaD_growstack(L, n, 1); pos; } \
|
||||
else { condmovestack(L,pre,pos); }
|
||||
else { condmovestack(L,pre,pos); }
|
||||
|
||||
/* In general, 'pre'/'pos' are empty (nothing to save) */
|
||||
#define luaD_checkstack(L,n) luaD_checkstackaux(L,n,(void)0,(void)0)
|
||||
|
||||
|
||||
|
||||
#define savestack(L,p) ((char *)(p) - (char *)L->stack)
|
||||
#define restorestack(L,n) ((StkId)((char *)L->stack + (n)))
|
||||
#define savestack(L,pt) (cast_charp(pt) - cast_charp(L->stack.p))
|
||||
#define restorestack(L,n) cast(StkId, cast_charp(L->stack.p) + (n))
|
||||
|
||||
|
||||
/* macro to check stack size, preserving 'p' */
|
||||
#define checkstackGCp(L,n,p) \
|
||||
#define checkstackp(L,n,p) \
|
||||
luaD_checkstackaux(L, n, \
|
||||
ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \
|
||||
luaC_checkGC(L), /* stack grow uses memory */ \
|
||||
ptrdiff_t t__ = savestack(L, p), /* save 'p' */ \
|
||||
p = restorestack(L, t__)) /* 'pos' part: restore 'p' */
|
||||
|
||||
|
||||
/* macro to check stack size and GC */
|
||||
#define checkstackGC(L,fsize) \
|
||||
luaD_checkstackaux(L, (fsize), luaC_checkGC(L), (void)0)
|
||||
/*
|
||||
** Maximum depth for nested C calls, syntactical nested non-terminals,
|
||||
** and other features implemented through recursion in C. (Value must
|
||||
** fit in a 16-bit unsigned integer. It must also be compatible with
|
||||
** the size of the C stack.)
|
||||
*/
|
||||
#if !defined(LUAI_MAXCCALLS)
|
||||
#define LUAI_MAXCCALLS 200
|
||||
#endif
|
||||
|
||||
|
||||
/* 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,
|
||||
LUAI_FUNC l_noret luaD_errerr (lua_State *L);
|
||||
LUAI_FUNC void luaD_seterrorobj (lua_State *L, TStatus errcode, StkId oldtop);
|
||||
LUAI_FUNC TStatus 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,
|
||||
int fTransfer, int nTransfer);
|
||||
LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci);
|
||||
LUAI_FUNC void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int n);
|
||||
LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func,
|
||||
int narg1, int delta);
|
||||
LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults);
|
||||
LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
|
||||
LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
|
||||
LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func);
|
||||
LUAI_FUNC int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status);
|
||||
LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
|
||||
LUAI_FUNC TStatus luaD_closeprotected (lua_State *L, ptrdiff_t level,
|
||||
TStatus status);
|
||||
LUAI_FUNC TStatus luaD_pcall (lua_State *L, Pfunc func, void *u,
|
||||
ptrdiff_t oldtop, ptrdiff_t ef);
|
||||
LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres);
|
||||
LUAI_FUNC int luaD_reallocstack (lua_State *L, int newsize, int raiseerror);
|
||||
@@ -72,8 +90,9 @@ LUAI_FUNC int luaD_growstack (lua_State *L, int n, int raiseerror);
|
||||
LUAI_FUNC void luaD_shrinkstack (lua_State *L);
|
||||
LUAI_FUNC void luaD_inctop (lua_State *L);
|
||||
|
||||
LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode);
|
||||
LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud);
|
||||
LUAI_FUNC l_noret luaD_throw (lua_State *L, TStatus errcode);
|
||||
LUAI_FUNC l_noret luaD_throwbaselevel (lua_State *L, TStatus errcode);
|
||||
LUAI_FUNC TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
155
ldump.c
155
ldump.c
@@ -10,12 +10,16 @@
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lapi.h"
|
||||
#include "lgc.h"
|
||||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
#include "ltable.h"
|
||||
#include "lundump.h"
|
||||
|
||||
|
||||
@@ -23,8 +27,11 @@ typedef struct {
|
||||
lua_State *L;
|
||||
lua_Writer writer;
|
||||
void *data;
|
||||
size_t offset; /* current position relative to beginning of dump */
|
||||
int strip;
|
||||
int status;
|
||||
Table *h; /* table to track saved strings */
|
||||
lua_Unsigned nstr; /* counter for counting saved strings */
|
||||
} DumpState;
|
||||
|
||||
|
||||
@@ -37,15 +44,36 @@ typedef struct {
|
||||
#define dumpLiteral(D, s) dumpBlock(D,s,sizeof(s) - sizeof(char))
|
||||
|
||||
|
||||
/*
|
||||
** Dump the block of memory pointed by 'b' with given 'size'.
|
||||
** 'b' should not be NULL, except for the last call signaling the end
|
||||
** of the dump.
|
||||
*/
|
||||
static void dumpBlock (DumpState *D, const void *b, size_t size) {
|
||||
if (D->status == 0 && size > 0) {
|
||||
if (D->status == 0) { /* do not write anything after an error */
|
||||
lua_unlock(D->L);
|
||||
D->status = (*D->writer)(D->L, b, size, D->data);
|
||||
lua_lock(D->L);
|
||||
D->offset += size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Dump enough zeros to ensure that current position is a multiple of
|
||||
** 'align'.
|
||||
*/
|
||||
static void dumpAlign (DumpState *D, unsigned align) {
|
||||
unsigned padding = align - cast_uint(D->offset % align);
|
||||
if (padding < align) { /* padding == align means no padding */
|
||||
static lua_Integer paddingContent = 0;
|
||||
lua_assert(align <= sizeof(lua_Integer));
|
||||
dumpBlock(D, &paddingContent, padding);
|
||||
}
|
||||
lua_assert(D->offset % align == 0);
|
||||
}
|
||||
|
||||
|
||||
#define dumpVar(D,x) dumpVector(D,&x,1)
|
||||
|
||||
|
||||
@@ -55,23 +83,33 @@ static void dumpByte (DumpState *D, int y) {
|
||||
}
|
||||
|
||||
|
||||
/* dumpInt Buff Size */
|
||||
#define DIBS ((sizeof(size_t) * 8 / 7) + 1)
|
||||
/*
|
||||
** size for 'dumpVarint' buffer: each byte can store up to 7 bits.
|
||||
** (The "+6" rounds up the division.)
|
||||
*/
|
||||
#define DIBS ((l_numbits(lua_Unsigned) + 6) / 7)
|
||||
|
||||
static void dumpSize (DumpState *D, size_t x) {
|
||||
/*
|
||||
** Dumps an unsigned integer using the MSB Varint encoding
|
||||
*/
|
||||
static void dumpVarint (DumpState *D, lua_Unsigned x) {
|
||||
lu_byte buff[DIBS];
|
||||
int n = 0;
|
||||
do {
|
||||
buff[DIBS - (++n)] = x & 0x7f; /* fill buffer in reverse order */
|
||||
x >>= 7;
|
||||
} while (x != 0);
|
||||
buff[DIBS - 1] |= 0x80; /* mark last byte */
|
||||
unsigned n = 1;
|
||||
buff[DIBS - 1] = x & 0x7f; /* fill least-significant byte */
|
||||
while ((x >>= 7) != 0) /* fill other bytes in reverse order */
|
||||
buff[DIBS - (++n)] = cast_byte((x & 0x7f) | 0x80);
|
||||
dumpVector(D, buff + DIBS - n, n);
|
||||
}
|
||||
|
||||
|
||||
static void dumpSize (DumpState *D, size_t sz) {
|
||||
dumpVarint(D, cast(lua_Unsigned, sz));
|
||||
}
|
||||
|
||||
|
||||
static void dumpInt (DumpState *D, int x) {
|
||||
dumpSize(D, x);
|
||||
lua_assert(x >= 0);
|
||||
dumpVarint(D, cast_uint(x));
|
||||
}
|
||||
|
||||
|
||||
@@ -80,30 +118,61 @@ static void dumpNumber (DumpState *D, lua_Number x) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Signed integers are coded to keep small values small. (Coding -1 as
|
||||
** 0xfff...fff would use too many bytes to save a quite common value.)
|
||||
** A non-negative x is coded as 2x; a negative x is coded as -2x - 1.
|
||||
** (0 => 0; -1 => 1; 1 => 2; -2 => 3; 2 => 4; ...)
|
||||
*/
|
||||
static void dumpInteger (DumpState *D, lua_Integer x) {
|
||||
dumpVar(D, x);
|
||||
lua_Unsigned cx = (x >= 0) ? 2u * l_castS2U(x)
|
||||
: (2u * ~l_castS2U(x)) + 1;
|
||||
dumpVarint(D, cx);
|
||||
}
|
||||
|
||||
|
||||
static void dumpString (DumpState *D, const TString *s) {
|
||||
if (s == NULL)
|
||||
/*
|
||||
** Dump a String. First dump its "size": size==0 means NULL;
|
||||
** size==1 is followed by an index and means "reuse saved string with
|
||||
** that index"; size>=2 is followed by the string contents with real
|
||||
** size==size-2 and means that string, which will be saved with
|
||||
** the next available index.
|
||||
*/
|
||||
static void dumpString (DumpState *D, TString *ts) {
|
||||
if (ts == NULL)
|
||||
dumpSize(D, 0);
|
||||
else {
|
||||
size_t size = tsslen(s);
|
||||
const char *str = getstr(s);
|
||||
dumpSize(D, size + 1);
|
||||
dumpVector(D, str, size);
|
||||
TValue idx;
|
||||
int tag = luaH_getstr(D->h, ts, &idx);
|
||||
if (!tagisempty(tag)) { /* string already saved? */
|
||||
dumpVarint(D, 1); /* reuse a saved string */
|
||||
dumpVarint(D, l_castS2U(ivalue(&idx))); /* index of saved string */
|
||||
}
|
||||
else { /* must write and save the string */
|
||||
TValue key, value; /* to save the string in the hash */
|
||||
size_t size;
|
||||
const char *s = getlstr(ts, size);
|
||||
dumpSize(D, size + 2);
|
||||
dumpVector(D, s, size + 1); /* include ending '\0' */
|
||||
D->nstr++; /* one more saved string */
|
||||
setsvalue(D->L, &key, ts); /* the string is the key */
|
||||
setivalue(&value, l_castU2S(D->nstr)); /* its index is the value */
|
||||
luaH_set(D->L, D->h, &key, &value); /* h[ts] = nstr */
|
||||
/* integer value does not need barrier */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void dumpCode (DumpState *D, const Proto *f) {
|
||||
dumpInt(D, f->sizecode);
|
||||
dumpVector(D, f->code, f->sizecode);
|
||||
dumpAlign(D, sizeof(f->code[0]));
|
||||
lua_assert(f->code != NULL);
|
||||
dumpVector(D, f->code, cast_uint(f->sizecode));
|
||||
}
|
||||
|
||||
|
||||
static void dumpFunction(DumpState *D, const Proto *f, TString *psource);
|
||||
static void dumpFunction (DumpState *D, const Proto *f);
|
||||
|
||||
static void dumpConstants (DumpState *D, const Proto *f) {
|
||||
int i;
|
||||
@@ -136,7 +205,7 @@ static void dumpProtos (DumpState *D, const Proto *f) {
|
||||
int n = f->sizep;
|
||||
dumpInt(D, n);
|
||||
for (i = 0; i < n; i++)
|
||||
dumpFunction(D, f->p[i], f->source);
|
||||
dumpFunction(D, f->p[i]);
|
||||
}
|
||||
|
||||
|
||||
@@ -155,12 +224,14 @@ static void dumpDebug (DumpState *D, const Proto *f) {
|
||||
int i, n;
|
||||
n = (D->strip) ? 0 : f->sizelineinfo;
|
||||
dumpInt(D, n);
|
||||
dumpVector(D, f->lineinfo, n);
|
||||
if (f->lineinfo != NULL)
|
||||
dumpVector(D, f->lineinfo, cast_uint(n));
|
||||
n = (D->strip) ? 0 : f->sizeabslineinfo;
|
||||
dumpInt(D, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
dumpInt(D, f->abslineinfo[i].pc);
|
||||
dumpInt(D, f->abslineinfo[i].line);
|
||||
if (n > 0) {
|
||||
/* 'abslineinfo' is an array of structures of int's */
|
||||
dumpAlign(D, sizeof(int));
|
||||
dumpVector(D, f->abslineinfo, cast_uint(n));
|
||||
}
|
||||
n = (D->strip) ? 0 : f->sizelocvars;
|
||||
dumpInt(D, n);
|
||||
@@ -176,51 +247,57 @@ static void dumpDebug (DumpState *D, const Proto *f) {
|
||||
}
|
||||
|
||||
|
||||
static void dumpFunction (DumpState *D, const Proto *f, TString *psource) {
|
||||
if (D->strip || f->source == psource)
|
||||
dumpString(D, NULL); /* no debug info or same source as its parent */
|
||||
else
|
||||
dumpString(D, f->source);
|
||||
static void dumpFunction (DumpState *D, const Proto *f) {
|
||||
dumpInt(D, f->linedefined);
|
||||
dumpInt(D, f->lastlinedefined);
|
||||
dumpByte(D, f->numparams);
|
||||
dumpByte(D, f->is_vararg);
|
||||
dumpByte(D, f->flag);
|
||||
dumpByte(D, f->maxstacksize);
|
||||
dumpCode(D, f);
|
||||
dumpConstants(D, f);
|
||||
dumpUpvalues(D, f);
|
||||
dumpProtos(D, f);
|
||||
dumpString(D, D->strip ? NULL : f->source);
|
||||
dumpDebug(D, f);
|
||||
}
|
||||
|
||||
|
||||
#define dumpNumInfo(D, tvar, value) \
|
||||
{ tvar i = value; dumpByte(D, sizeof(tvar)); dumpVar(D, i); }
|
||||
|
||||
|
||||
static void dumpHeader (DumpState *D) {
|
||||
dumpLiteral(D, LUA_SIGNATURE);
|
||||
dumpByte(D, LUAC_VERSION);
|
||||
dumpByte(D, LUAC_FORMAT);
|
||||
dumpLiteral(D, LUAC_DATA);
|
||||
dumpByte(D, sizeof(Instruction));
|
||||
dumpByte(D, sizeof(lua_Integer));
|
||||
dumpByte(D, sizeof(lua_Number));
|
||||
dumpInteger(D, LUAC_INT);
|
||||
dumpNumber(D, LUAC_NUM);
|
||||
dumpNumInfo(D, int, LUAC_INT);
|
||||
dumpNumInfo(D, Instruction, LUAC_INST);
|
||||
dumpNumInfo(D, lua_Integer, LUAC_INT);
|
||||
dumpNumInfo(D, lua_Number, LUAC_NUM);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** dump Lua function as precompiled chunk
|
||||
*/
|
||||
int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, void *data,
|
||||
int strip) {
|
||||
int luaU_dump (lua_State *L, const Proto *f, lua_Writer w, void *data,
|
||||
int strip) {
|
||||
DumpState D;
|
||||
D.h = luaH_new(L); /* aux. table to keep strings already dumped */
|
||||
sethvalue2s(L, L->top.p, D.h); /* anchor it */
|
||||
L->top.p++;
|
||||
D.L = L;
|
||||
D.writer = w;
|
||||
D.offset = 0;
|
||||
D.data = data;
|
||||
D.strip = strip;
|
||||
D.status = 0;
|
||||
D.nstr = 0;
|
||||
dumpHeader(&D);
|
||||
dumpByte(&D, f->sizeupvalues);
|
||||
dumpFunction(&D, f, NULL);
|
||||
dumpFunction(&D, f);
|
||||
dumpBlock(&D, NULL, 0); /* signal end of dump */
|
||||
return D.status;
|
||||
}
|
||||
|
||||
|
||||
127
lfunc.c
127
lfunc.c
@@ -50,8 +50,8 @@ void luaF_initupvals (lua_State *L, LClosure *cl) {
|
||||
for (i = 0; i < cl->nupvalues; i++) {
|
||||
GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal));
|
||||
UpVal *uv = gco2upv(o);
|
||||
uv->v = &uv->u.value; /* make it closed */
|
||||
setnilvalue(uv->v);
|
||||
uv->v.p = &uv->u.value; /* make it closed */
|
||||
setnilvalue(uv->v.p);
|
||||
cl->upvals[i] = uv;
|
||||
luaC_objbarrier(L, cl, uv);
|
||||
}
|
||||
@@ -62,12 +62,11 @@ void luaF_initupvals (lua_State *L, LClosure *cl) {
|
||||
** 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) {
|
||||
static UpVal *newupval (lua_State *L, StkId level, UpVal **prev) {
|
||||
GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal));
|
||||
UpVal *uv = gco2upv(o);
|
||||
UpVal *next = *prev;
|
||||
uv->v = s2v(level); /* current value lives in the stack */
|
||||
uv->tbc = tbc;
|
||||
uv->v.p = s2v(level); /* current value lives in the stack */
|
||||
uv->u.open.next = next; /* link it to list of open upvalues */
|
||||
uv->u.open.previous = prev;
|
||||
if (next)
|
||||
@@ -96,26 +95,28 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
|
||||
pp = &p->u.open.next;
|
||||
}
|
||||
/* not found: create a new upvalue after 'pp' */
|
||||
return newupval(L, 0, level, pp);
|
||||
return newupval(L, level, pp);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Call closing method for object 'obj' with error message 'err'. The
|
||||
** Call closing method for object 'obj' with error object 'err'. The
|
||||
** boolean 'yy' controls whether the call is yieldable.
|
||||
** (This function assumes EXTRA_STACK.)
|
||||
*/
|
||||
static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) {
|
||||
StkId top = L->top;
|
||||
StkId top = L->top.p;
|
||||
StkId func = top;
|
||||
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
|
||||
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 */
|
||||
setobj2s(L, top++, tm); /* will call metamethod... */
|
||||
setobj2s(L, top++, obj); /* with 'self' as the 1st argument */
|
||||
if (err != NULL) /* if there was an error... */
|
||||
setobj2s(L, top++, err); /* then error object will be 2nd argument */
|
||||
L->top.p = top; /* add function and arguments */
|
||||
if (yy)
|
||||
luaD_call(L, top, 0);
|
||||
luaD_call(L, func, 0);
|
||||
else
|
||||
luaD_callnoyield(L, top, 0);
|
||||
luaD_callnoyield(L, func, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -126,7 +127,7 @@ static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) {
|
||||
static void checkclosemth (lua_State *L, StkId level) {
|
||||
const TValue *tm = luaT_gettmbyobj(L, s2v(level), TM_CLOSE);
|
||||
if (ttisnil(tm)) { /* no metamethod? */
|
||||
int idx = cast_int(level - L->ci->func); /* variable index */
|
||||
int idx = cast_int(level - L->ci->func.p); /* variable index */
|
||||
const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
|
||||
if (vname == NULL) vname = "?";
|
||||
luaG_runerror(L, "variable '%s' got a non-closable value", vname);
|
||||
@@ -141,42 +142,44 @@ static void checkclosemth (lua_State *L, StkId level) {
|
||||
** the 'level' of the upvalue being closed, as everything after that
|
||||
** won't be used again.
|
||||
*/
|
||||
static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) {
|
||||
static void prepcallclosemth (lua_State *L, StkId level, TStatus status,
|
||||
int yy) {
|
||||
TValue *uv = s2v(level); /* value being closed */
|
||||
TValue *errobj;
|
||||
if (status == CLOSEKTOP)
|
||||
errobj = &G(L)->nilvalue; /* error object is nil */
|
||||
else { /* 'luaD_seterrorobj' will set top to level + 2 */
|
||||
errobj = s2v(level + 1); /* error object goes after 'uv' */
|
||||
luaD_seterrorobj(L, status, level + 1); /* set error object */
|
||||
switch (status) {
|
||||
case LUA_OK:
|
||||
L->top.p = level + 1; /* call will be at this level */
|
||||
/* FALLTHROUGH */
|
||||
case CLOSEKTOP: /* don't need to change top */
|
||||
errobj = NULL; /* no error object */
|
||||
break;
|
||||
default: /* 'luaD_seterrorobj' will set top to level + 2 */
|
||||
errobj = s2v(level + 1); /* error object goes after 'uv' */
|
||||
luaD_seterrorobj(L, status, level + 1); /* set error object */
|
||||
break;
|
||||
}
|
||||
callclosemethod(L, uv, errobj, yy);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Maximum value for deltas in 'tbclist', dependent on the type
|
||||
** of delta. (This macro assumes that an 'L' is in scope where it
|
||||
** is used.)
|
||||
*/
|
||||
#define MAXDELTA \
|
||||
((256ul << ((sizeof(L->stack->tbclist.delta) - 1) * 8)) - 1)
|
||||
/* Maximum value for deltas in 'tbclist' */
|
||||
#define MAXDELTA USHRT_MAX
|
||||
|
||||
|
||||
/*
|
||||
** Insert a variable in the list of to-be-closed variables.
|
||||
*/
|
||||
void luaF_newtbcupval (lua_State *L, StkId level) {
|
||||
lua_assert(level > L->tbclist);
|
||||
lua_assert(level > L->tbclist.p);
|
||||
if (l_isfalse(s2v(level)))
|
||||
return; /* false doesn't need to be closed */
|
||||
checkclosemth(L, level); /* value must have a close method */
|
||||
while (cast_uint(level - L->tbclist) > MAXDELTA) {
|
||||
L->tbclist += MAXDELTA; /* create a dummy node at maximum delta */
|
||||
L->tbclist->tbclist.delta = 0;
|
||||
while (cast_uint(level - L->tbclist.p) > MAXDELTA) {
|
||||
L->tbclist.p += MAXDELTA; /* create a dummy node at maximum delta */
|
||||
L->tbclist.p->tbclist.delta = 0;
|
||||
}
|
||||
level->tbclist.delta = cast(unsigned short, level - L->tbclist);
|
||||
L->tbclist = level;
|
||||
level->tbclist.delta = cast(unsigned short, level - L->tbclist.p);
|
||||
L->tbclist.p = level;
|
||||
}
|
||||
|
||||
|
||||
@@ -196,10 +199,10 @@ void luaF_closeupval (lua_State *L, StkId level) {
|
||||
StkId upl; /* stack index pointed by 'uv' */
|
||||
while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) {
|
||||
TValue *slot = &uv->u.value; /* new position for value */
|
||||
lua_assert(uplevel(uv) < L->top);
|
||||
lua_assert(uplevel(uv) < L->top.p);
|
||||
luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */
|
||||
setobj(L, slot, uv->v); /* move value to upvalue slot */
|
||||
uv->v = slot; /* now current value lives here */
|
||||
setobj(L, slot, uv->v.p); /* move value to upvalue slot */
|
||||
uv->v.p = slot; /* now current value lives here */
|
||||
if (!iswhite(uv)) { /* neither white nor dead? */
|
||||
nw2black(uv); /* closed upvalues cannot be gray */
|
||||
luaC_barrier(L, uv, slot);
|
||||
@@ -209,31 +212,32 @@ void luaF_closeupval (lua_State *L, StkId level) {
|
||||
|
||||
|
||||
/*
|
||||
** Remove firt element from the tbclist plus its dummy nodes.
|
||||
** Remove first element from the tbclist plus its dummy nodes.
|
||||
*/
|
||||
static void poptbclist (lua_State *L) {
|
||||
StkId tbc = L->tbclist;
|
||||
StkId tbc = L->tbclist.p;
|
||||
lua_assert(tbc->tbclist.delta > 0); /* first element cannot be dummy */
|
||||
tbc -= tbc->tbclist.delta;
|
||||
while (tbc > L->stack && tbc->tbclist.delta == 0)
|
||||
while (tbc > L->stack.p && tbc->tbclist.delta == 0)
|
||||
tbc -= MAXDELTA; /* remove dummy nodes */
|
||||
L->tbclist = tbc;
|
||||
L->tbclist.p = tbc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Close all upvalues and to-be-closed variables up to the given stack
|
||||
** level.
|
||||
** level. Return restored 'level'.
|
||||
*/
|
||||
void luaF_close (lua_State *L, StkId level, int status, int yy) {
|
||||
StkId luaF_close (lua_State *L, StkId level, TStatus status, int yy) {
|
||||
ptrdiff_t levelrel = savestack(L, level);
|
||||
luaF_closeupval(L, level); /* first, close the upvalues */
|
||||
while (L->tbclist >= level) { /* traverse tbc's down to that level */
|
||||
StkId tbc = L->tbclist; /* get variable index */
|
||||
while (L->tbclist.p >= level) { /* traverse tbc's down to that level */
|
||||
StkId tbc = L->tbclist.p; /* get variable index */
|
||||
poptbclist(L); /* remove it from list */
|
||||
prepcallclosemth(L, tbc, status, yy); /* close variable */
|
||||
level = restorestack(L, levelrel);
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
|
||||
@@ -253,7 +257,7 @@ Proto *luaF_newproto (lua_State *L) {
|
||||
f->upvalues = NULL;
|
||||
f->sizeupvalues = 0;
|
||||
f->numparams = 0;
|
||||
f->is_vararg = 0;
|
||||
f->flag = 0;
|
||||
f->maxstacksize = 0;
|
||||
f->locvars = NULL;
|
||||
f->sizelocvars = 0;
|
||||
@@ -264,14 +268,31 @@ Proto *luaF_newproto (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
lu_mem luaF_protosize (Proto *p) {
|
||||
lu_mem sz = cast(lu_mem, sizeof(Proto))
|
||||
+ cast_uint(p->sizep) * sizeof(Proto*)
|
||||
+ cast_uint(p->sizek) * sizeof(TValue)
|
||||
+ cast_uint(p->sizelocvars) * sizeof(LocVar)
|
||||
+ cast_uint(p->sizeupvalues) * sizeof(Upvaldesc);
|
||||
if (!(p->flag & PF_FIXED)) {
|
||||
sz += cast_uint(p->sizecode) * sizeof(Instruction);
|
||||
sz += cast_uint(p->sizelineinfo) * sizeof(lu_byte);
|
||||
sz += cast_uint(p->sizeabslineinfo) * sizeof(AbsLineInfo);
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
|
||||
void luaF_freeproto (lua_State *L, Proto *f) {
|
||||
luaM_freearray(L, f->code, f->sizecode);
|
||||
luaM_freearray(L, f->p, f->sizep);
|
||||
luaM_freearray(L, f->k, f->sizek);
|
||||
luaM_freearray(L, f->lineinfo, f->sizelineinfo);
|
||||
luaM_freearray(L, f->abslineinfo, f->sizeabslineinfo);
|
||||
luaM_freearray(L, f->locvars, f->sizelocvars);
|
||||
luaM_freearray(L, f->upvalues, f->sizeupvalues);
|
||||
if (!(f->flag & PF_FIXED)) {
|
||||
luaM_freearray(L, f->code, cast_sizet(f->sizecode));
|
||||
luaM_freearray(L, f->lineinfo, cast_sizet(f->sizelineinfo));
|
||||
luaM_freearray(L, f->abslineinfo, cast_sizet(f->sizeabslineinfo));
|
||||
}
|
||||
luaM_freearray(L, f->p, cast_sizet(f->sizep));
|
||||
luaM_freearray(L, f->k, cast_sizet(f->sizek));
|
||||
luaM_freearray(L, f->locvars, cast_sizet(f->sizelocvars));
|
||||
luaM_freearray(L, f->upvalues, cast_sizet(f->sizeupvalues));
|
||||
luaM_free(L, f);
|
||||
}
|
||||
|
||||
|
||||
17
lfunc.h
17
lfunc.h
@@ -11,11 +11,11 @@
|
||||
#include "lobject.h"
|
||||
|
||||
|
||||
#define sizeCclosure(n) (cast_int(offsetof(CClosure, upvalue)) + \
|
||||
cast_int(sizeof(TValue)) * (n))
|
||||
#define sizeCclosure(n) \
|
||||
(offsetof(CClosure, upvalue) + sizeof(TValue) * cast_uint(n))
|
||||
|
||||
#define sizeLclosure(n) (cast_int(offsetof(LClosure, upvals)) + \
|
||||
cast_int(sizeof(TValue *)) * (n))
|
||||
#define sizeLclosure(n) \
|
||||
(offsetof(LClosure, upvals) + sizeof(UpVal *) * cast_uint(n))
|
||||
|
||||
|
||||
/* test whether thread is in 'twups' list */
|
||||
@@ -29,10 +29,10 @@
|
||||
#define MAXUPVAL 255
|
||||
|
||||
|
||||
#define upisopen(up) ((up)->v != &(up)->u.value)
|
||||
#define upisopen(up) ((up)->v.p != &(up)->u.value)
|
||||
|
||||
|
||||
#define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v))
|
||||
#define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v.p))
|
||||
|
||||
|
||||
/*
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
|
||||
/* special status to close upvalues preserving the top of the stack */
|
||||
#define CLOSEKTOP (-1)
|
||||
#define CLOSEKTOP (LUA_ERRERR + 1)
|
||||
|
||||
|
||||
LUAI_FUNC Proto *luaF_newproto (lua_State *L);
|
||||
@@ -54,8 +54,9 @@ LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl);
|
||||
LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
|
||||
LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level);
|
||||
LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level);
|
||||
LUAI_FUNC void luaF_close (lua_State *L, StkId level, int status, int yy);
|
||||
LUAI_FUNC StkId luaF_close (lua_State *L, StkId level, TStatus status, int yy);
|
||||
LUAI_FUNC void luaF_unlinkupval (UpVal *uv);
|
||||
LUAI_FUNC lu_mem luaF_protosize (Proto *p);
|
||||
LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
|
||||
LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
|
||||
int pc);
|
||||
|
||||
151
lgc.h
151
lgc.h
@@ -8,6 +8,9 @@
|
||||
#define lgc_h
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
|
||||
@@ -20,8 +23,9 @@
|
||||
** never point to a white one. Moreover, any gray object must be in a
|
||||
** "gray list" (gray, grayagain, weak, allweak, ephemeron) so that it
|
||||
** can be visited again before finishing the collection cycle. (Open
|
||||
** upvalues are an exception to this rule.) These lists have no meaning
|
||||
** when the invariant is not being enforced (e.g., sweep phase).
|
||||
** upvalues are an exception to this rule, as they are attached to
|
||||
** a corresponding thread.) These lists have no meaning when the
|
||||
** invariant is not being enforced (e.g., sweep phase).
|
||||
*/
|
||||
|
||||
|
||||
@@ -45,10 +49,10 @@
|
||||
|
||||
/*
|
||||
** macro to tell when main invariant (white objects cannot point to black
|
||||
** ones) must be kept. During a collection, the sweep
|
||||
** phase may break the invariant, as objects turned white may point to
|
||||
** still-black objects. The invariant is restored when sweep ends and
|
||||
** all objects are white again.
|
||||
** ones) must be kept. During a collection, the sweep phase may break
|
||||
** the invariant, as objects turned white may point to still-black
|
||||
** objects. The invariant is restored when sweep ends and all objects
|
||||
** are white again.
|
||||
*/
|
||||
|
||||
#define keepinvariant(g) ((g)->gcstate <= GCSatomic)
|
||||
@@ -117,69 +121,144 @@
|
||||
#define setage(o,a) ((o)->marked = cast_byte(((o)->marked & (~AGEBITS)) | a))
|
||||
#define isold(o) (getage(o) > G_SURVIVAL)
|
||||
|
||||
#define changeage(o,f,t) \
|
||||
check_exp(getage(o) == (f), (o)->marked ^= ((f)^(t)))
|
||||
|
||||
/*
|
||||
** In generational mode, objects are created 'new'. After surviving one
|
||||
** cycle, they become 'survival'. Both 'new' and 'survival' can point
|
||||
** to any other object, as they are traversed at the end of the cycle.
|
||||
** We call them both 'young' objects.
|
||||
** If a survival object survives another cycle, it becomes 'old1'.
|
||||
** 'old1' objects can still point to survival objects (but not to
|
||||
** new objects), so they still must be traversed. After another cycle
|
||||
** (that, being old, 'old1' objects will "survive" no matter what)
|
||||
** finally the 'old1' object becomes really 'old', and then they
|
||||
** are no more traversed.
|
||||
**
|
||||
** To keep its invariants, the generational mode uses the same barriers
|
||||
** also used by the incremental mode. If a young object is caught in a
|
||||
** forward barrier, it cannot become old immediately, because it can
|
||||
** still point to other young objects. Instead, it becomes 'old0',
|
||||
** which in the next cycle becomes 'old1'. So, 'old0' objects is
|
||||
** old but can point to new and survival objects; 'old1' is old
|
||||
** but cannot point to new objects; and 'old' cannot point to any
|
||||
** young object.
|
||||
**
|
||||
** If any old object ('old0', 'old1', 'old') is caught in a back
|
||||
** barrier, it becomes 'touched1' and goes into a gray list, to be
|
||||
** visited at the end of the cycle. There it evolves to 'touched2',
|
||||
** which can point to survivals but not to new objects. In yet another
|
||||
** cycle then it becomes 'old' again.
|
||||
**
|
||||
** The generational mode must also control the colors of objects,
|
||||
** because of the barriers. While the mutator is running, young objects
|
||||
** are kept white. 'old', 'old1', and 'touched2' objects are kept black,
|
||||
** as they cannot point to new objects; exceptions are threads and open
|
||||
** upvalues, which age to 'old1' and 'old' but are kept gray. 'old0'
|
||||
** objects may be gray or black, as in the incremental mode. 'touched1'
|
||||
** objects are kept gray, as they must be visited again at the end of
|
||||
** the cycle.
|
||||
*/
|
||||
|
||||
|
||||
/* Default Values for GC parameters */
|
||||
#define LUAI_GENMAJORMUL 100
|
||||
/*
|
||||
** {======================================================
|
||||
** Default Values for GC parameters
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
** Minor collections will shift to major ones after LUAI_MINORMAJOR%
|
||||
** bytes become old.
|
||||
*/
|
||||
#define LUAI_MINORMAJOR 70
|
||||
|
||||
/*
|
||||
** Major collections will shift to minor ones after a collection
|
||||
** collects at least LUAI_MAJORMINOR% of the new bytes.
|
||||
*/
|
||||
#define LUAI_MAJORMINOR 50
|
||||
|
||||
/*
|
||||
** A young (minor) collection will run after creating LUAI_GENMINORMUL%
|
||||
** new bytes.
|
||||
*/
|
||||
#define LUAI_GENMINORMUL 20
|
||||
|
||||
/* wait memory to double before starting new cycle */
|
||||
#define LUAI_GCPAUSE 200
|
||||
|
||||
/* incremental */
|
||||
|
||||
/* Number of bytes must be LUAI_GCPAUSE% before starting new cycle */
|
||||
#define LUAI_GCPAUSE 250
|
||||
|
||||
/*
|
||||
** some gc parameters are stored divided by 4 to allow a maximum value
|
||||
** up to 1023 in a 'lu_byte'.
|
||||
** Step multiplier: The collector handles LUAI_GCMUL% work units for
|
||||
** each new allocated word. (Each "work unit" corresponds roughly to
|
||||
** sweeping one object or traversing one slot.)
|
||||
*/
|
||||
#define getgcparam(p) ((p) * 4)
|
||||
#define setgcparam(p,v) ((p) = (v) / 4)
|
||||
#define LUAI_GCMUL 200
|
||||
|
||||
#define LUAI_GCMUL 100
|
||||
/* How many bytes to allocate before next GC step */
|
||||
#define LUAI_GCSTEPSIZE (200 * sizeof(Table))
|
||||
|
||||
/* how much to allocate before next GC step (log2) */
|
||||
#define LUAI_GCSTEPSIZE 13 /* 8 KB */
|
||||
|
||||
#define setgcparam(g,p,v) (g->gcparams[LUA_GCP##p] = luaO_codeparam(v))
|
||||
#define applygcparam(g,p,x) luaO_applyparam(g->gcparams[LUA_GCP##p], x)
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** 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'.
|
||||
** Control when GC is running:
|
||||
*/
|
||||
#define isdecGCmodegen(g) (g->gckind == KGC_GEN || g->lastatomic != 0)
|
||||
#define GCSTPUSR 1 /* bit true when GC stopped by user */
|
||||
#define GCSTPGC 2 /* bit true when GC stopped by itself */
|
||||
#define GCSTPCLS 4 /* bit true when closing Lua state */
|
||||
#define gcrunning(g) ((g)->gcstp == 0)
|
||||
|
||||
|
||||
/*
|
||||
** Does one step of collection when debt becomes positive. 'pre'/'pos'
|
||||
** Does one step of collection when debt becomes zero. 'pre'/'pos'
|
||||
** allows some adjustments to be done only when needed. macro
|
||||
** 'condchangemem' is used only for heavy tests (forcing a full
|
||||
** GC cycle on every opportunity)
|
||||
*/
|
||||
|
||||
#if !defined(HARDMEMTESTS)
|
||||
#define condchangemem(L,pre,pos,emg) ((void)0)
|
||||
#else
|
||||
#define condchangemem(L,pre,pos,emg) \
|
||||
{ if (gcrunning(G(L))) { pre; luaC_fullgc(L, emg); pos; } }
|
||||
#endif
|
||||
|
||||
#define luaC_condGC(L,pre,pos) \
|
||||
{ if (G(L)->GCdebt > 0) { pre; luaC_step(L); pos;}; \
|
||||
condchangemem(L,pre,pos); }
|
||||
{ if (G(L)->GCdebt <= 0) { pre; luaC_step(L); pos;}; \
|
||||
condchangemem(L,pre,pos,0); }
|
||||
|
||||
/* more often than not, 'pre'/'pos' are empty */
|
||||
#define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0)
|
||||
|
||||
|
||||
#define luaC_barrier(L,p,v) ( \
|
||||
(iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \
|
||||
luaC_barrier_(L,obj2gco(p),gcvalue(v)) : cast_void(0))
|
||||
|
||||
#define luaC_barrierback(L,p,v) ( \
|
||||
(iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \
|
||||
luaC_barrierback_(L,p) : cast_void(0))
|
||||
|
||||
#define luaC_objbarrier(L,p,o) ( \
|
||||
(isblack(p) && iswhite(o)) ? \
|
||||
luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0))
|
||||
|
||||
#define luaC_barrier(L,p,v) ( \
|
||||
iscollectable(v) ? luaC_objbarrier(L,p,gcvalue(v)) : cast_void(0))
|
||||
|
||||
#define luaC_objbarrierback(L,p,o) ( \
|
||||
(isblack(p) && iswhite(o)) ? luaC_barrierback_(L,p) : cast_void(0))
|
||||
|
||||
#define luaC_barrierback(L,p,v) ( \
|
||||
iscollectable(v) ? luaC_objbarrierback(L, p, gcvalue(v)) : 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);
|
||||
LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask);
|
||||
LUAI_FUNC void luaC_runtilstate (lua_State *L, int state, int fast);
|
||||
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 GCObject *luaC_newobj (lua_State *L, lu_byte tt, size_t sz);
|
||||
LUAI_FUNC GCObject *luaC_newobjdt (lua_State *L, lu_byte tt, size_t sz,
|
||||
size_t offset);
|
||||
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_checkfinalizer (lua_State *L, GCObject *o, Table *mt);
|
||||
|
||||
50
linit.c
50
linit.c
@@ -8,21 +8,6 @@
|
||||
#define linit_c
|
||||
#define LUA_LIB
|
||||
|
||||
/*
|
||||
** If you embed Lua in your program and need to open the standard
|
||||
** libraries, call luaL_openlibs in your program. If you need a
|
||||
** different set of libraries, copy this file to your project and edit
|
||||
** it to suit your needs.
|
||||
**
|
||||
** You can also *preload* libraries, so that a later 'require' can
|
||||
** open the library, which is already linked to the application.
|
||||
** For that, do the following code:
|
||||
**
|
||||
** luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
|
||||
** lua_pushcfunction(L, luaopen_modname);
|
||||
** lua_setfield(L, -2, modname);
|
||||
** lua_pop(L, 1); // remove PRELOAD table
|
||||
*/
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
@@ -33,33 +18,46 @@
|
||||
|
||||
#include "lualib.h"
|
||||
#include "lauxlib.h"
|
||||
#include "llimits.h"
|
||||
|
||||
|
||||
/*
|
||||
** these libs are loaded by lua.c and are readily available to any Lua
|
||||
** program
|
||||
** Standard Libraries. (Must be listed in the same ORDER of their
|
||||
** respective constants LUA_<libname>K.)
|
||||
*/
|
||||
static const luaL_Reg loadedlibs[] = {
|
||||
static const luaL_Reg stdlibs[] = {
|
||||
{LUA_GNAME, luaopen_base},
|
||||
{LUA_LOADLIBNAME, luaopen_package},
|
||||
{LUA_COLIBNAME, luaopen_coroutine},
|
||||
{LUA_TABLIBNAME, luaopen_table},
|
||||
{LUA_DBLIBNAME, luaopen_debug},
|
||||
{LUA_IOLIBNAME, luaopen_io},
|
||||
{LUA_MATHLIBNAME, luaopen_math},
|
||||
{LUA_OSLIBNAME, luaopen_os},
|
||||
{LUA_STRLIBNAME, luaopen_string},
|
||||
{LUA_MATHLIBNAME, luaopen_math},
|
||||
{LUA_TABLIBNAME, luaopen_table},
|
||||
{LUA_UTF8LIBNAME, luaopen_utf8},
|
||||
{LUA_DBLIBNAME, luaopen_debug},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
LUALIB_API void luaL_openlibs (lua_State *L) {
|
||||
/*
|
||||
** require and preload selected standard libraries
|
||||
*/
|
||||
LUALIB_API void luaL_openselectedlibs (lua_State *L, int load, int preload) {
|
||||
int mask;
|
||||
const luaL_Reg *lib;
|
||||
/* "require" functions from 'loadedlibs' and set results to global table */
|
||||
for (lib = loadedlibs; lib->func; lib++) {
|
||||
luaL_requiref(L, lib->name, lib->func, 1);
|
||||
lua_pop(L, 1); /* remove lib */
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
|
||||
for (lib = stdlibs, mask = 1; lib->name != NULL; lib++, mask <<= 1) {
|
||||
if (load & mask) { /* selected? */
|
||||
luaL_requiref(L, lib->name, lib->func, 1); /* require library */
|
||||
lua_pop(L, 1); /* remove result from the stack */
|
||||
}
|
||||
else if (preload & mask) { /* selected? */
|
||||
lua_pushcfunction(L, lib->func);
|
||||
lua_setfield(L, -2, lib->name); /* add library to PRELOAD table */
|
||||
}
|
||||
}
|
||||
lua_assert((mask >> 1) == LUA_UTF8LIBK);
|
||||
lua_pop(L, 1); /* remove PRELOAD table */
|
||||
}
|
||||
|
||||
|
||||
75
liolib.c
75
liolib.c
@@ -21,8 +21,7 @@
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
|
||||
#include "llimits.h"
|
||||
|
||||
|
||||
/*
|
||||
@@ -245,8 +244,8 @@ static int f_gc (lua_State *L) {
|
||||
*/
|
||||
static int io_fclose (lua_State *L) {
|
||||
LStream *p = tolstream(L);
|
||||
int res = fclose(p->f);
|
||||
return luaL_fileresult(L, (res == 0), NULL);
|
||||
errno = 0;
|
||||
return luaL_fileresult(L, (fclose(p->f) == 0), NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -272,6 +271,7 @@ static int io_open (lua_State *L) {
|
||||
LStream *p = newfile(L);
|
||||
const char *md = mode; /* to traverse/check mode */
|
||||
luaL_argcheck(L, l_checkmode(md), 2, "invalid mode");
|
||||
errno = 0;
|
||||
p->f = fopen(filename, mode);
|
||||
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
|
||||
}
|
||||
@@ -292,6 +292,7 @@ static int io_popen (lua_State *L) {
|
||||
const char *mode = luaL_optstring(L, 2, "r");
|
||||
LStream *p = newprefile(L);
|
||||
luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode");
|
||||
errno = 0;
|
||||
p->f = l_popen(L, filename, mode);
|
||||
p->closef = &io_pclose;
|
||||
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
|
||||
@@ -300,6 +301,7 @@ static int io_popen (lua_State *L) {
|
||||
|
||||
static int io_tmpfile (lua_State *L) {
|
||||
LStream *p = newfile(L);
|
||||
errno = 0;
|
||||
p->f = tmpfile();
|
||||
return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1;
|
||||
}
|
||||
@@ -441,7 +443,7 @@ static int nextc (RN *rn) {
|
||||
return 0; /* fail */
|
||||
}
|
||||
else {
|
||||
rn->buff[rn->n++] = rn->c; /* save current char */
|
||||
rn->buff[rn->n++] = cast_char(rn->c); /* save current char */
|
||||
rn->c = l_getc(rn->f); /* read next one */
|
||||
return 1;
|
||||
}
|
||||
@@ -522,15 +524,15 @@ static int read_line (lua_State *L, FILE *f, int chop) {
|
||||
luaL_buffinit(L, &b);
|
||||
do { /* may need to read several chunks to get whole line */
|
||||
char *buff = luaL_prepbuffer(&b); /* preallocate buffer space */
|
||||
int i = 0;
|
||||
unsigned 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; /* read up to end of line or buffer limit */
|
||||
buff[i++] = cast_char(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_addchar(&b, '\n'); /* add ending newline to result */
|
||||
luaL_pushresult(&b); /* close buffer */
|
||||
/* return ok if read something (either a newline or something else) */
|
||||
return (c == '\n' || lua_rawlen(L, -1) > 0);
|
||||
@@ -567,6 +569,7 @@ static int g_read (lua_State *L, FILE *f, int first) {
|
||||
int nargs = lua_gettop(L) - 1;
|
||||
int n, success;
|
||||
clearerr(f);
|
||||
errno = 0;
|
||||
if (nargs == 0) { /* no arguments? */
|
||||
success = read_line(L, f, 1);
|
||||
n = first + 1; /* to return 1 result */
|
||||
@@ -659,26 +662,28 @@ static int io_readline (lua_State *L) {
|
||||
|
||||
static int g_write (lua_State *L, FILE *f, int arg) {
|
||||
int nargs = lua_gettop(L) - arg;
|
||||
int status = 1;
|
||||
for (; nargs--; arg++) {
|
||||
if (lua_type(L, arg) == LUA_TNUMBER) {
|
||||
/* optimization: could be done exactly as for strings */
|
||||
int len = lua_isinteger(L, arg)
|
||||
? fprintf(f, LUA_INTEGER_FMT,
|
||||
(LUAI_UACINT)lua_tointeger(L, arg))
|
||||
: fprintf(f, LUA_NUMBER_FMT,
|
||||
(LUAI_UACNUMBER)lua_tonumber(L, arg));
|
||||
status = status && (len > 0);
|
||||
size_t totalbytes = 0; /* total number of bytes written */
|
||||
errno = 0;
|
||||
for (; nargs--; arg++) { /* for each argument */
|
||||
char buff[LUA_N2SBUFFSZ];
|
||||
const char *s;
|
||||
size_t numbytes; /* bytes written in one call to 'fwrite' */
|
||||
size_t len = lua_numbertocstring(L, arg, buff); /* try as a number */
|
||||
if (len > 0) { /* did conversion work (value was a number)? */
|
||||
s = buff;
|
||||
len--;
|
||||
}
|
||||
else {
|
||||
size_t l;
|
||||
const char *s = luaL_checklstring(L, arg, &l);
|
||||
status = status && (fwrite(s, sizeof(char), l, f) == l);
|
||||
else /* must be a string */
|
||||
s = luaL_checklstring(L, arg, &len);
|
||||
numbytes = fwrite(s, sizeof(char), len, f);
|
||||
totalbytes += numbytes;
|
||||
if (numbytes < len) { /* write error? */
|
||||
int n = luaL_fileresult(L, 0, NULL);
|
||||
lua_pushinteger(L, cast_st2S(totalbytes));
|
||||
return n + 1; /* return fail, error msg., error code, and counter */
|
||||
}
|
||||
}
|
||||
if (l_likely(status))
|
||||
return 1; /* file handle already on stack top */
|
||||
else return luaL_fileresult(L, status, NULL);
|
||||
return 1; /* no errors; file handle already on stack top */
|
||||
}
|
||||
|
||||
|
||||
@@ -703,6 +708,7 @@ static int f_seek (lua_State *L) {
|
||||
l_seeknum offset = (l_seeknum)p3;
|
||||
luaL_argcheck(L, (lua_Integer)offset == p3, 3,
|
||||
"not an integer in proper range");
|
||||
errno = 0;
|
||||
op = l_fseek(f, offset, mode[op]);
|
||||
if (l_unlikely(op))
|
||||
return luaL_fileresult(L, 0, NULL); /* error */
|
||||
@@ -719,19 +725,26 @@ static int f_setvbuf (lua_State *L) {
|
||||
FILE *f = tofile(L);
|
||||
int op = luaL_checkoption(L, 2, NULL, modenames);
|
||||
lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
|
||||
int res = setvbuf(f, NULL, mode[op], (size_t)sz);
|
||||
int res;
|
||||
errno = 0;
|
||||
res = setvbuf(f, NULL, mode[op], (size_t)sz);
|
||||
return luaL_fileresult(L, res == 0, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int io_flush (lua_State *L) {
|
||||
return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL);
|
||||
static int aux_flush (lua_State *L, FILE *f) {
|
||||
errno = 0;
|
||||
return luaL_fileresult(L, fflush(f) == 0, NULL);
|
||||
}
|
||||
|
||||
|
||||
static int f_flush (lua_State *L) {
|
||||
return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL);
|
||||
return aux_flush(L, tofile(L));
|
||||
}
|
||||
|
||||
|
||||
static int io_flush (lua_State *L) {
|
||||
return aux_flush(L, getiofile(L, IO_OUTPUT));
|
||||
}
|
||||
|
||||
|
||||
@@ -773,7 +786,7 @@ static const luaL_Reg meth[] = {
|
||||
** metamethods for file handles
|
||||
*/
|
||||
static const luaL_Reg metameth[] = {
|
||||
{"__index", NULL}, /* place holder */
|
||||
{"__index", NULL}, /* placeholder */
|
||||
{"__gc", f_gc},
|
||||
{"__close", f_gc},
|
||||
{"__tostring", f_tostring},
|
||||
|
||||
89
llex.c
89
llex.c
@@ -32,6 +32,11 @@
|
||||
#define next(ls) (ls->current = zgetc(ls->z))
|
||||
|
||||
|
||||
/* minimum size for string buffer */
|
||||
#if !defined(LUA_MINBUFFER)
|
||||
#define LUA_MINBUFFER 32
|
||||
#endif
|
||||
|
||||
|
||||
#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r')
|
||||
|
||||
@@ -39,7 +44,7 @@
|
||||
/* ORDER RESERVED */
|
||||
static const char *const luaX_tokens [] = {
|
||||
"and", "break", "do", "else", "elseif",
|
||||
"end", "false", "for", "function", "goto", "if",
|
||||
"end", "false", "for", "function", "global", "goto", "if",
|
||||
"in", "local", "nil", "not", "or", "repeat",
|
||||
"return", "then", "true", "until", "while",
|
||||
"//", "..", "...", "==", ">=", "<=", "~=",
|
||||
@@ -57,10 +62,10 @@ static l_noret lexerror (LexState *ls, const char *msg, int token);
|
||||
static void save (LexState *ls, int c) {
|
||||
Mbuffer *b = ls->buff;
|
||||
if (luaZ_bufflen(b) + 1 > luaZ_sizebuffer(b)) {
|
||||
size_t newsize;
|
||||
if (luaZ_sizebuffer(b) >= MAX_SIZE/2)
|
||||
size_t newsize = luaZ_sizebuffer(b); /* get old size */;
|
||||
if (newsize >= (MAX_SIZE/3 * 2)) /* larger than MAX_SIZE/1.5 ? */
|
||||
lexerror(ls, "lexical element too long", 0);
|
||||
newsize = luaZ_sizebuffer(b) * 2;
|
||||
newsize += (newsize >> 1); /* new size is 1.5 times the old one */
|
||||
luaZ_resizebuffer(ls->L, b, newsize);
|
||||
}
|
||||
b->buffer[luaZ_bufflen(b)++] = cast_char(c);
|
||||
@@ -122,30 +127,34 @@ l_noret luaX_syntaxerror (LexState *ls, const char *msg) {
|
||||
|
||||
|
||||
/*
|
||||
** Creates a new string and anchors it in scanner's table so that it
|
||||
** will not be collected until the end of the compilation; by that time
|
||||
** it should be anchored somewhere. It also internalizes long strings,
|
||||
** ensuring there is only one copy of each unique string. The table
|
||||
** here is used as a set: the string enters as the key, while its value
|
||||
** is irrelevant. We use the string itself as the value only because it
|
||||
** is a TValue readly available. Later, the code generation can change
|
||||
** this value.
|
||||
** Anchors a string in scanner's table so that it will not be collected
|
||||
** until the end of the compilation; by that time it should be anchored
|
||||
** somewhere. It also internalizes long strings, ensuring there is only
|
||||
** one copy of each unique string.
|
||||
*/
|
||||
TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
|
||||
static TString *anchorstr (LexState *ls, TString *ts) {
|
||||
lua_State *L = ls->L;
|
||||
TString *ts = luaS_newlstr(L, str, l); /* create new string */
|
||||
const TValue *o = luaH_getstr(ls->h, ts);
|
||||
if (!ttisnil(o)) /* string already present? */
|
||||
ts = keystrval(nodefromval(o)); /* get saved copy */
|
||||
else { /* not in use yet */
|
||||
TValue *stv = s2v(L->top++); /* reserve stack space for string */
|
||||
setsvalue(L, stv, ts); /* temporarily anchor the string */
|
||||
luaH_finishset(L, ls->h, stv, o, stv); /* t[string] = string */
|
||||
TValue oldts;
|
||||
int tag = luaH_getstr(ls->h, ts, &oldts);
|
||||
if (!tagisempty(tag)) /* string already present? */
|
||||
return tsvalue(&oldts); /* use stored value */
|
||||
else { /* create a new entry */
|
||||
TValue *stv = s2v(L->top.p++); /* reserve stack space for string */
|
||||
setsvalue(L, stv, ts); /* push (anchor) the string on the stack */
|
||||
luaH_set(L, ls->h, stv, stv); /* t[string] = string */
|
||||
/* table is not a metatable, so it does not need to invalidate cache */
|
||||
luaC_checkGC(L);
|
||||
L->top--; /* remove string from stack */
|
||||
L->top.p--; /* remove string from stack */
|
||||
return ts;
|
||||
}
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Creates a new string and anchors it in scanner's table.
|
||||
*/
|
||||
TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
|
||||
return anchorstr(ls, luaS_newlstr(ls->L, str, l));
|
||||
}
|
||||
|
||||
|
||||
@@ -159,7 +168,7 @@ static void inclinenumber (LexState *ls) {
|
||||
next(ls); /* skip '\n' or '\r' */
|
||||
if (currIsNewline(ls) && ls->current != old)
|
||||
next(ls); /* skip '\n\r' or '\r\n' */
|
||||
if (++ls->linenumber >= MAX_INT)
|
||||
if (++ls->linenumber >= INT_MAX)
|
||||
lexerror(ls, "chunk has too many lines", 0);
|
||||
}
|
||||
|
||||
@@ -175,7 +184,15 @@ void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source,
|
||||
ls->linenumber = 1;
|
||||
ls->lastline = 1;
|
||||
ls->source = source;
|
||||
ls->envn = luaS_newliteral(L, LUA_ENV); /* get env name */
|
||||
/* all three strings here ("_ENV", "break", "global") were fixed,
|
||||
so they cannot be collected */
|
||||
ls->envn = luaS_newliteral(L, LUA_ENV); /* get env string */
|
||||
ls->brkn = luaS_newliteral(L, "break"); /* get "break" string */
|
||||
#if defined(LUA_COMPAT_GLOBAL)
|
||||
/* compatibility mode: "global" is not a reserved word */
|
||||
ls->glbn = luaS_newliteral(L, "global"); /* get "global" string */
|
||||
ls->glbn->extra = 0; /* mark it as not reserved */
|
||||
#endif
|
||||
luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */
|
||||
}
|
||||
|
||||
@@ -340,12 +357,17 @@ static int readhexaesc (LexState *ls) {
|
||||
}
|
||||
|
||||
|
||||
static unsigned long readutf8esc (LexState *ls) {
|
||||
unsigned long r;
|
||||
int i = 4; /* chars to be removed: '\', 'u', '{', and first digit */
|
||||
/*
|
||||
** When reading a UTF-8 escape sequence, save everything to the buffer
|
||||
** for error reporting in case of errors; 'i' counts the number of
|
||||
** saved characters, so that they can be removed if case of success.
|
||||
*/
|
||||
static l_uint32 readutf8esc (LexState *ls) {
|
||||
l_uint32 r;
|
||||
int i = 4; /* number of chars to be removed: start with #"\u{X" */
|
||||
save_and_next(ls); /* skip 'u' */
|
||||
esccheck(ls, ls->current == '{', "missing '{'");
|
||||
r = gethexa(ls); /* must have at least one digit */
|
||||
r = cast_uint(gethexa(ls)); /* must have at least one digit */
|
||||
while (cast_void(save_and_next(ls)), lisxdigit(ls->current)) {
|
||||
i++;
|
||||
esccheck(ls, r <= (0x7FFFFFFFu >> 4), "UTF-8 value too large");
|
||||
@@ -542,12 +564,13 @@ static int llex (LexState *ls, SemInfo *seminfo) {
|
||||
do {
|
||||
save_and_next(ls);
|
||||
} while (lislalnum(ls->current));
|
||||
ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
|
||||
luaZ_bufflen(ls->buff));
|
||||
seminfo->ts = ts;
|
||||
if (isreserved(ts)) /* reserved word? */
|
||||
/* find or create string */
|
||||
ts = luaS_newlstr(ls->L, luaZ_buffer(ls->buff),
|
||||
luaZ_bufflen(ls->buff));
|
||||
if (isreserved(ts)) /* reserved word? */
|
||||
return ts->extra - 1 + FIRST_RESERVED;
|
||||
else {
|
||||
seminfo->ts = anchorstr(ls, ts);
|
||||
return TK_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
8
llex.h
8
llex.h
@@ -33,8 +33,8 @@ enum RESERVED {
|
||||
/* terminal symbols denoted by reserved words */
|
||||
TK_AND = FIRST_RESERVED, TK_BREAK,
|
||||
TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
|
||||
TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
|
||||
TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
|
||||
TK_GLOBAL, TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR,
|
||||
TK_REPEAT, TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
|
||||
/* other terminal symbols */
|
||||
TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE,
|
||||
TK_SHL, TK_SHR,
|
||||
@@ -59,7 +59,7 @@ typedef struct Token {
|
||||
} Token;
|
||||
|
||||
|
||||
/* state of the lexer plus state of the parser when shared by all
|
||||
/* state of the scanner plus state of the parser when shared by all
|
||||
functions */
|
||||
typedef struct LexState {
|
||||
int current; /* current character (charint) */
|
||||
@@ -75,6 +75,8 @@ typedef struct LexState {
|
||||
struct Dyndata *dyd; /* dynamic structures used by the parser */
|
||||
TString *source; /* current source name */
|
||||
TString *envn; /* environment variable name */
|
||||
TString *brkn; /* "break" name (used as a label) */
|
||||
TString *glbn; /* "global" name (when not a reserved word) */
|
||||
} LexState;
|
||||
|
||||
|
||||
|
||||
268
llimits.h
268
llimits.h
@@ -15,49 +15,55 @@
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
#define l_numbits(t) cast_int(sizeof(t) * CHAR_BIT)
|
||||
|
||||
/*
|
||||
** '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
|
||||
** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines.
|
||||
** 'l_mem' is a signed integer big enough to count the total memory
|
||||
** used by Lua. (It is signed due to the use of debt in several
|
||||
** computations.) Usually, 'ptrdiff_t' should work, but we use 'long'
|
||||
** for 16-bit machines.
|
||||
*/
|
||||
#if defined(LUAI_MEM) /* { external definitions? */
|
||||
typedef LUAI_UMEM lu_mem;
|
||||
typedef LUAI_MEM l_mem;
|
||||
typedef LUAI_UMEM lu_mem;
|
||||
#elif LUAI_IS32INT /* }{ */
|
||||
typedef size_t lu_mem;
|
||||
typedef ptrdiff_t l_mem;
|
||||
typedef size_t lu_mem;
|
||||
#else /* 16-bit ints */ /* }{ */
|
||||
typedef unsigned long lu_mem;
|
||||
typedef long l_mem;
|
||||
typedef unsigned long lu_mem;
|
||||
#endif /* } */
|
||||
|
||||
#define MAX_LMEM \
|
||||
cast(l_mem, (cast(lu_mem, 1) << (l_numbits(l_mem) - 1)) - 1)
|
||||
|
||||
|
||||
/* chars used as small naturals (so that 'char' is reserved for characters) */
|
||||
typedef unsigned char lu_byte;
|
||||
typedef signed char ls_byte;
|
||||
|
||||
|
||||
/* Type for thread status/error codes */
|
||||
typedef lu_byte TStatus;
|
||||
|
||||
/* The C API still uses 'int' for status/error codes */
|
||||
#define APIstatus(st) cast_int(st)
|
||||
|
||||
/* 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 for strings and userdata visible for Lua; should be
|
||||
** representable as a lua_Integer and as a size_t.
|
||||
*/
|
||||
#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \
|
||||
: (size_t)(LUA_MAXINTEGER))
|
||||
|
||||
|
||||
#define MAX_LUMEM ((lu_mem)(~(lu_mem)0))
|
||||
|
||||
#define MAX_LMEM ((l_mem)(MAX_LUMEM >> 1))
|
||||
|
||||
|
||||
#define MAX_INT INT_MAX /* maximum value of an int */
|
||||
|
||||
: cast_sizet(LUA_MAXINTEGER))
|
||||
|
||||
/*
|
||||
** floor of the log2 of the maximum signed value for integral type 't'.
|
||||
** (That is, maximum 'n' such that '2^n' fits in the given signed type.)
|
||||
*/
|
||||
#define log2maxs(t) (sizeof(t) * 8 - 2)
|
||||
#define log2maxs(t) (l_numbits(t) - 2)
|
||||
|
||||
|
||||
/*
|
||||
@@ -71,11 +77,24 @@ typedef signed char ls_byte;
|
||||
|
||||
|
||||
/*
|
||||
** conversion of pointer to unsigned integer:
|
||||
** this is for hashing only; there is no problem if the integer
|
||||
** cannot hold the whole pointer value
|
||||
** conversion of pointer to unsigned integer: this is for hashing only;
|
||||
** there is no problem if the integer cannot hold the whole pointer
|
||||
** value. (In strict ISO C this may cause undefined behavior, but no
|
||||
** actual machine seems to bother.)
|
||||
*/
|
||||
#define point2uint(p) ((unsigned int)((size_t)(p) & UINT_MAX))
|
||||
#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \
|
||||
__STDC_VERSION__ >= 199901L
|
||||
#include <stdint.h>
|
||||
#if defined(UINTPTR_MAX) /* even in C99 this type is optional */
|
||||
#define L_P2I uintptr_t
|
||||
#else /* no 'intptr'? */
|
||||
#define L_P2I uintmax_t /* use the largest available integer */
|
||||
#endif
|
||||
#else /* C89 option */
|
||||
#define L_P2I size_t
|
||||
#endif
|
||||
|
||||
#define point2uint(p) cast_uint((L_P2I)(p) & UINT_MAX)
|
||||
|
||||
|
||||
|
||||
@@ -91,26 +110,18 @@ typedef LUAI_UACINT l_uacInt;
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
#define lua_assert(c) assert(c)
|
||||
#define assert_code(c) c
|
||||
#endif
|
||||
|
||||
#if defined(lua_assert)
|
||||
#define check_exp(c,e) (lua_assert(c), (e))
|
||||
/* to avoid problems with conditions too long */
|
||||
#define lua_longassert(c) ((c) ? (void)0 : lua_assert(0))
|
||||
#else
|
||||
#define lua_assert(c) ((void)0)
|
||||
#define check_exp(c,e) (e)
|
||||
#define lua_longassert(c) ((void)0)
|
||||
#define assert_code(c) ((void)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** assertion for checking API calls
|
||||
*/
|
||||
#if !defined(luai_apicheck)
|
||||
#define luai_apicheck(l,e) ((void)l, lua_assert(e))
|
||||
#endif
|
||||
|
||||
#define api_check(l,e,msg) luai_apicheck(l,(e) && msg)
|
||||
#define check_exp(c,e) (lua_assert(c), (e))
|
||||
/* to avoid problems with conditions too long */
|
||||
#define lua_longassert(c) assert_code((c) ? (void)0 : lua_assert(0))
|
||||
|
||||
|
||||
/* macro to avoid warnings about unused variables */
|
||||
@@ -126,12 +137,15 @@ typedef LUAI_UACINT l_uacInt;
|
||||
#define cast_voidp(i) cast(void *, (i))
|
||||
#define cast_num(i) cast(lua_Number, (i))
|
||||
#define cast_int(i) cast(int, (i))
|
||||
#define cast_short(i) cast(short, (i))
|
||||
#define cast_uint(i) cast(unsigned int, (i))
|
||||
#define cast_byte(i) cast(lu_byte, (i))
|
||||
#define cast_uchar(i) cast(unsigned char, (i))
|
||||
#define cast_char(i) cast(char, (i))
|
||||
#define cast_charp(i) cast(char *, (i))
|
||||
#define cast_sizet(i) cast(size_t, (i))
|
||||
#define cast_Integer(i) cast(lua_Integer, (i))
|
||||
#define cast_Inst(i) cast(Instruction, (i))
|
||||
|
||||
|
||||
/* cast a signed lua_Integer to lua_Unsigned */
|
||||
@@ -148,6 +162,38 @@ typedef LUAI_UACINT l_uacInt;
|
||||
#define l_castU2S(i) ((lua_Integer)(i))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** cast a size_t to lua_Integer: These casts are always valid for
|
||||
** sizes of Lua objects (see MAX_SIZE)
|
||||
*/
|
||||
#define cast_st2S(sz) ((lua_Integer)(sz))
|
||||
|
||||
/* Cast a ptrdiff_t to size_t, when it is known that the minuend
|
||||
** comes from the subtrahend (the base)
|
||||
*/
|
||||
#define ct_diff2sz(df) ((size_t)(df))
|
||||
|
||||
/* ptrdiff_t to lua_Integer */
|
||||
#define ct_diff2S(df) cast_st2S(ct_diff2sz(df))
|
||||
|
||||
/*
|
||||
** Special type equivalent to '(void*)' for functions (to suppress some
|
||||
** warnings when converting function pointers)
|
||||
*/
|
||||
typedef void (*voidf)(void);
|
||||
|
||||
/*
|
||||
** Macro to convert pointer-to-void* to pointer-to-function. This cast
|
||||
** is undefined according to ISO C, but POSIX assumes that it works.
|
||||
** (The '__extension__' in gnu compilers is only to avoid warnings.)
|
||||
*/
|
||||
#if defined(__GNUC__)
|
||||
#define cast_func(p) (__extension__ (voidf)(p))
|
||||
#else
|
||||
#define cast_func(p) ((voidf)(p))
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** non-return type
|
||||
@@ -166,8 +212,21 @@ typedef LUAI_UACINT l_uacInt;
|
||||
|
||||
|
||||
/*
|
||||
** type for virtual-machine instructions;
|
||||
** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)
|
||||
** Inline functions
|
||||
*/
|
||||
#if !defined(LUA_USE_C89)
|
||||
#define l_inline inline
|
||||
#elif defined(__GNUC__)
|
||||
#define l_inline __inline__
|
||||
#else
|
||||
#define l_inline /* empty */
|
||||
#endif
|
||||
|
||||
#define l_sinline static l_inline
|
||||
|
||||
|
||||
/*
|
||||
** An unsigned with (at least) 4 bytes
|
||||
*/
|
||||
#if LUAI_IS32INT
|
||||
typedef unsigned int l_uint32;
|
||||
@@ -175,107 +234,6 @@ typedef unsigned int l_uint32;
|
||||
typedef unsigned long l_uint32;
|
||||
#endif
|
||||
|
||||
typedef l_uint32 Instruction;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Maximum length for short strings, that is, strings that are
|
||||
** internalized. (Cannot be smaller than reserved words or tags for
|
||||
** metamethods, as these strings must be internalized;
|
||||
** #("function") = 8, #("__newindex") = 10.)
|
||||
*/
|
||||
#if !defined(LUAI_MAXSHORTLEN)
|
||||
#define LUAI_MAXSHORTLEN 40
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Initial size for the string table (must be power of 2).
|
||||
** The Lua core alone registers ~50 strings (reserved words +
|
||||
** metaevent keys + a few others). Libraries would typically add
|
||||
** a few dozens more.
|
||||
*/
|
||||
#if !defined(MINSTRTABSIZE)
|
||||
#define MINSTRTABSIZE 128
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Size of cache for strings in the API. 'N' is the number of
|
||||
** sets (better be a prime) and "M" is the size of each set (M == 1
|
||||
** makes a direct cache.)
|
||||
*/
|
||||
#if !defined(STRCACHE_N)
|
||||
#define STRCACHE_N 53
|
||||
#define STRCACHE_M 2
|
||||
#endif
|
||||
|
||||
|
||||
/* minimum size for string buffer */
|
||||
#if !defined(LUA_MINBUFFER)
|
||||
#define LUA_MINBUFFER 32
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Maximum depth for nested C calls, syntactical nested non-terminals,
|
||||
** and other features implemented through recursion in C. (Value must
|
||||
** fit in a 16-bit unsigned integer. It must also be compatible with
|
||||
** the size of the C stack.)
|
||||
*/
|
||||
#if !defined(LUAI_MAXCCALLS)
|
||||
#define LUAI_MAXCCALLS 200
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** macros that are executed whenever program enters the Lua core
|
||||
** ('lua_lock') and leaves the core ('lua_unlock')
|
||||
*/
|
||||
#if !defined(lua_lock)
|
||||
#define lua_lock(L) ((void) 0)
|
||||
#define lua_unlock(L) ((void) 0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** macro executed during Lua functions at points where the
|
||||
** function can yield.
|
||||
*/
|
||||
#if !defined(luai_threadyield)
|
||||
#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** these macros allow user-specific actions when a thread is
|
||||
** created/deleted/resumed/yielded.
|
||||
*/
|
||||
#if !defined(luai_userstateopen)
|
||||
#define luai_userstateopen(L) ((void)L)
|
||||
#endif
|
||||
|
||||
#if !defined(luai_userstateclose)
|
||||
#define luai_userstateclose(L) ((void)L)
|
||||
#endif
|
||||
|
||||
#if !defined(luai_userstatethread)
|
||||
#define luai_userstatethread(L,L1) ((void)L)
|
||||
#endif
|
||||
|
||||
#if !defined(luai_userstatefree)
|
||||
#define luai_userstatefree(L,L1) ((void)L)
|
||||
#endif
|
||||
|
||||
#if !defined(luai_userstateresume)
|
||||
#define luai_userstateresume(L,n) ((void)L)
|
||||
#endif
|
||||
|
||||
#if !defined(luai_userstateyield)
|
||||
#define luai_userstateyield(L,n) ((void)L)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** The luai_num* macros define the primitive operations over numbers.
|
||||
@@ -329,25 +287,29 @@ typedef l_uint32 Instruction;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** macro to control inclusion of some hard tests on stack reallocation
|
||||
** {==================================================================
|
||||
** "Abstraction Layer" for basic report of messages and errors
|
||||
** ===================================================================
|
||||
*/
|
||||
#if !defined(HARDSTACKTESTS)
|
||||
#define condmovestack(L,pre,pos) ((void)0)
|
||||
#else
|
||||
/* realloc stack keeping its size */
|
||||
#define condmovestack(L,pre,pos) \
|
||||
{ int sz_ = stacksize(L); pre; luaD_reallocstack((L), sz_, 0); pos; }
|
||||
|
||||
/* print a string */
|
||||
#if !defined(lua_writestring)
|
||||
#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout)
|
||||
#endif
|
||||
|
||||
#if !defined(HARDMEMTESTS)
|
||||
#define condchangemem(L,pre,pos) ((void)0)
|
||||
#else
|
||||
#define condchangemem(L,pre,pos) \
|
||||
{ if (G(L)->gcrunning) { pre; luaC_fullgc(L, 0); pos; } }
|
||||
/* print a newline and flush the output */
|
||||
#if !defined(lua_writeline)
|
||||
#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout))
|
||||
#endif
|
||||
|
||||
/* print an error message */
|
||||
#if !defined(lua_writestringerror)
|
||||
#define lua_writestringerror(s,p) \
|
||||
(fprintf(stderr, (s), (p)), fflush(stderr))
|
||||
#endif
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
130
lmathlib.c
130
lmathlib.c
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
#include "llimits.h"
|
||||
|
||||
|
||||
#undef PI
|
||||
@@ -105,7 +106,7 @@ static int math_floor (lua_State *L) {
|
||||
|
||||
static int math_ceil (lua_State *L) {
|
||||
if (lua_isinteger(L, 1))
|
||||
lua_settop(L, 1); /* integer is its own ceil */
|
||||
lua_settop(L, 1); /* integer is its own ceiling */
|
||||
else {
|
||||
lua_Number d = l_mathop(ceil)(luaL_checknumber(L, 1));
|
||||
pushnumint(L, d);
|
||||
@@ -249,6 +250,15 @@ static int math_type (lua_State *L) {
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
** This code uses lots of shifts. ANSI C does not allow shifts greater
|
||||
** than or equal to the width of the type being shifted, so some shifts
|
||||
** are written in convoluted ways to match that restriction. For
|
||||
** preprocessor tests, it assumes a width of 32 bits, so the maximum
|
||||
** shift there is 31 bits.
|
||||
*/
|
||||
|
||||
|
||||
/* number of binary digits in the mantissa of a float */
|
||||
#define FIGS l_floatatt(MANT_DIG)
|
||||
|
||||
@@ -267,20 +277,23 @@ static int math_type (lua_State *L) {
|
||||
|
||||
/* try to find an integer type with at least 64 bits */
|
||||
|
||||
#if (ULONG_MAX >> 31 >> 31) >= 3
|
||||
#if ((ULONG_MAX >> 31) >> 31) >= 3
|
||||
|
||||
/* 'long' has at least 64 bits */
|
||||
#define Rand64 unsigned long
|
||||
#define SRand64 long
|
||||
|
||||
#elif !defined(LUA_USE_C89) && defined(LLONG_MAX)
|
||||
|
||||
/* there is a 'long long' type (which must have at least 64 bits) */
|
||||
#define Rand64 unsigned long long
|
||||
#define SRand64 long long
|
||||
|
||||
#elif (LUA_MAXUNSIGNED >> 31 >> 31) >= 3
|
||||
#elif ((LUA_MAXUNSIGNED >> 31) >> 31) >= 3
|
||||
|
||||
/* 'lua_Integer' has at least 64 bits */
|
||||
/* 'lua_Unsigned' has at least 64 bits */
|
||||
#define Rand64 lua_Unsigned
|
||||
#define SRand64 lua_Integer
|
||||
|
||||
#endif
|
||||
|
||||
@@ -319,23 +332,30 @@ static Rand64 nextrand (Rand64 *state) {
|
||||
}
|
||||
|
||||
|
||||
/* must take care to not shift stuff by more than 63 slots */
|
||||
|
||||
|
||||
/*
|
||||
** Convert bits from a random integer into a float in the
|
||||
** interval [0,1), getting the higher FIG bits from the
|
||||
** random unsigned integer and converting that to a float.
|
||||
** Some old Microsoft compilers cannot cast an unsigned long
|
||||
** to a floating-point number, so we use a signed long as an
|
||||
** intermediary. When lua_Number is float or double, the shift ensures
|
||||
** that 'sx' is non negative; in that case, a good compiler will remove
|
||||
** the correction.
|
||||
*/
|
||||
|
||||
/* must throw out the extra (64 - FIGS) bits */
|
||||
#define shift64_FIG (64 - FIGS)
|
||||
|
||||
/* to scale to [0, 1), multiply by scaleFIG = 2^(-FIGS) */
|
||||
/* 2^(-FIGS) == 2^-1 / 2^(FIGS-1) */
|
||||
#define scaleFIG (l_mathop(0.5) / ((Rand64)1 << (FIGS - 1)))
|
||||
|
||||
static lua_Number I2d (Rand64 x) {
|
||||
return (lua_Number)(trim64(x) >> shift64_FIG) * scaleFIG;
|
||||
SRand64 sx = (SRand64)(trim64(x) >> shift64_FIG);
|
||||
lua_Number res = (lua_Number)(sx) * scaleFIG;
|
||||
if (sx < 0)
|
||||
res += l_mathop(1.0); /* correct the two's complement if negative */
|
||||
lua_assert(0 <= res && res < 1);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* convert a 'Rand64' to a 'lua_Unsigned' */
|
||||
@@ -347,25 +367,17 @@ static lua_Number I2d (Rand64 x) {
|
||||
|
||||
#else /* no 'Rand64' }{ */
|
||||
|
||||
/* get an integer with at least 32 bits */
|
||||
#if LUAI_IS32INT
|
||||
typedef unsigned int lu_int32;
|
||||
#else
|
||||
typedef unsigned long lu_int32;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Use two 32-bit integers to represent a 64-bit quantity.
|
||||
*/
|
||||
typedef struct Rand64 {
|
||||
lu_int32 h; /* higher half */
|
||||
lu_int32 l; /* lower half */
|
||||
l_uint32 h; /* higher half */
|
||||
l_uint32 l; /* lower half */
|
||||
} Rand64;
|
||||
|
||||
|
||||
/*
|
||||
** If 'lu_int32' has more than 32 bits, the extra bits do not interfere
|
||||
** If 'l_uint32' has more than 32 bits, the extra bits do not interfere
|
||||
** with the 32 initial bits, except in a right shift and comparisons.
|
||||
** Moreover, the final result has to discard the extra bits.
|
||||
*/
|
||||
@@ -379,7 +391,7 @@ typedef struct Rand64 {
|
||||
*/
|
||||
|
||||
/* build a new Rand64 value */
|
||||
static Rand64 packI (lu_int32 h, lu_int32 l) {
|
||||
static Rand64 packI (l_uint32 h, l_uint32 l) {
|
||||
Rand64 result;
|
||||
result.h = h;
|
||||
result.l = l;
|
||||
@@ -452,7 +464,7 @@ static Rand64 nextrand (Rand64 *state) {
|
||||
*/
|
||||
|
||||
/* an unsigned 1 with proper type */
|
||||
#define UONE ((lu_int32)1)
|
||||
#define UONE ((l_uint32)1)
|
||||
|
||||
|
||||
#if FIGS <= 32
|
||||
@@ -471,11 +483,9 @@ static lua_Number I2d (Rand64 x) {
|
||||
|
||||
#else /* 32 < FIGS <= 64 */
|
||||
|
||||
/* must take care to not shift stuff by more than 31 slots */
|
||||
|
||||
/* 2^(-FIGS) = 1.0 / 2^30 / 2^3 / 2^(FIGS-33) */
|
||||
#define scaleFIG \
|
||||
((lua_Number)1.0 / (UONE << 30) / 8.0 / (UONE << (FIGS - 33)))
|
||||
(l_mathop(1.0) / (UONE << 30) / l_mathop(8.0) / (UONE << (FIGS - 33)))
|
||||
|
||||
/*
|
||||
** use FIGS - 32 bits from lower half, throwing out the other
|
||||
@@ -486,7 +496,7 @@ static lua_Number I2d (Rand64 x) {
|
||||
/*
|
||||
** higher 32 bits go after those (FIGS - 32) bits: shiftHI = 2^(FIGS - 32)
|
||||
*/
|
||||
#define shiftHI ((lua_Number)(UONE << (FIGS - 33)) * 2.0)
|
||||
#define shiftHI ((lua_Number)(UONE << (FIGS - 33)) * l_mathop(2.0))
|
||||
|
||||
|
||||
static lua_Number I2d (Rand64 x) {
|
||||
@@ -500,12 +510,12 @@ static lua_Number I2d (Rand64 x) {
|
||||
|
||||
/* convert a 'Rand64' to a 'lua_Unsigned' */
|
||||
static lua_Unsigned I2UInt (Rand64 x) {
|
||||
return ((lua_Unsigned)trim32(x.h) << 31 << 1) | (lua_Unsigned)trim32(x.l);
|
||||
return (((lua_Unsigned)trim32(x.h) << 31) << 1) | (lua_Unsigned)trim32(x.l);
|
||||
}
|
||||
|
||||
/* convert a 'lua_Unsigned' to a 'Rand64' */
|
||||
static Rand64 Int2I (lua_Unsigned n) {
|
||||
return packI((lu_int32)(n >> 31 >> 1), (lu_int32)n);
|
||||
return packI((l_uint32)((n >> 31) >> 1), (l_uint32)n);
|
||||
}
|
||||
|
||||
#endif /* } */
|
||||
@@ -523,7 +533,7 @@ typedef struct {
|
||||
** Project the random integer 'ran' into the interval [0, n].
|
||||
** Because 'ran' has 2^B possible values, the projection can only be
|
||||
** uniform when the size of the interval is a power of 2 (exact
|
||||
** division). Otherwise, to get a uniform projection into [0, n], we
|
||||
** division). So, to get a uniform projection into [0, n], we
|
||||
** first compute 'lim', the smallest Mersenne number not smaller than
|
||||
** 'n'. We then project 'ran' into the interval [0, lim]. If the result
|
||||
** is inside [0, n], we are done. Otherwise, we try with another 'ran',
|
||||
@@ -531,26 +541,14 @@ typedef struct {
|
||||
*/
|
||||
static lua_Unsigned project (lua_Unsigned ran, lua_Unsigned n,
|
||||
RanState *state) {
|
||||
if ((n & (n + 1)) == 0) /* is 'n + 1' a power of 2? */
|
||||
return ran & n; /* no bias */
|
||||
else {
|
||||
lua_Unsigned lim = n;
|
||||
/* compute the smallest (2^b - 1) not smaller than 'n' */
|
||||
lim |= (lim >> 1);
|
||||
lim |= (lim >> 2);
|
||||
lim |= (lim >> 4);
|
||||
lim |= (lim >> 8);
|
||||
lim |= (lim >> 16);
|
||||
#if (LUA_MAXUNSIGNED >> 31) >= 3
|
||||
lim |= (lim >> 32); /* integer type has more than 32 bits */
|
||||
#endif
|
||||
lua_assert((lim & (lim + 1)) == 0 /* 'lim + 1' is a power of 2, */
|
||||
&& lim >= n /* not smaller than 'n', */
|
||||
&& (lim >> 1) < n); /* and it is the smallest one */
|
||||
while ((ran &= lim) > n) /* project 'ran' into [0..lim] */
|
||||
ran = I2UInt(nextrand(state->s)); /* not inside [0..n]? try again */
|
||||
return ran;
|
||||
}
|
||||
lua_Unsigned lim = n; /* to compute the Mersenne number */
|
||||
int sh; /* how much to spread bits to the right in 'lim' */
|
||||
/* spread '1' bits in 'lim' until it becomes a Mersenne number */
|
||||
for (sh = 1; (lim & (lim + 1)) != 0; sh *= 2)
|
||||
lim |= (lim >> sh); /* spread '1's to the right */
|
||||
while ((ran &= lim) > n) /* project 'ran' into [0..lim] and test */
|
||||
ran = I2UInt(nextrand(state->s)); /* not inside [0..n]? try again */
|
||||
return ran;
|
||||
}
|
||||
|
||||
|
||||
@@ -568,7 +566,7 @@ static int math_random (lua_State *L) {
|
||||
low = 1;
|
||||
up = luaL_checkinteger(L, 1);
|
||||
if (up == 0) { /* single 0 as argument? */
|
||||
lua_pushinteger(L, I2UInt(rv)); /* full random integer */
|
||||
lua_pushinteger(L, l_castU2S(I2UInt(rv))); /* full random integer */
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
@@ -583,8 +581,8 @@ static int math_random (lua_State *L) {
|
||||
/* random integer in the interval [low, up] */
|
||||
luaL_argcheck(L, low <= up, 1, "interval is empty");
|
||||
/* project random integer into the interval [0, up - low] */
|
||||
p = project(I2UInt(rv), (lua_Unsigned)up - (lua_Unsigned)low, state);
|
||||
lua_pushinteger(L, p + (lua_Unsigned)low);
|
||||
p = project(I2UInt(rv), l_castS2U(up) - l_castS2U(low), state);
|
||||
lua_pushinteger(L, l_castU2S(p + l_castS2U(low)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -598,33 +596,23 @@ static void setseed (lua_State *L, Rand64 *state,
|
||||
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);
|
||||
lua_pushinteger(L, l_castU2S(n1));
|
||||
lua_pushinteger(L, l_castU2S(n2));
|
||||
}
|
||||
|
||||
|
||||
static int math_randomseed (lua_State *L) {
|
||||
RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1));
|
||||
lua_Unsigned n1, n2;
|
||||
if (lua_isnone(L, 1)) {
|
||||
randseed(L, state);
|
||||
n1 = luaL_makeseed(L); /* "random" seed */
|
||||
n2 = I2UInt(nextrand(state->s)); /* in case seed is not that random... */
|
||||
}
|
||||
else {
|
||||
lua_Integer n1 = luaL_checkinteger(L, 1);
|
||||
lua_Integer n2 = luaL_optinteger(L, 2, 0);
|
||||
setseed(L, state->s, n1, n2);
|
||||
n1 = l_castS2U(luaL_checkinteger(L, 1));
|
||||
n2 = l_castS2U(luaL_optinteger(L, 2, 0));
|
||||
}
|
||||
setseed(L, state->s, n1, n2);
|
||||
return 2; /* return seeds */
|
||||
}
|
||||
|
||||
@@ -641,7 +629,7 @@ static const luaL_Reg randfuncs[] = {
|
||||
*/
|
||||
static void setrandfunc (lua_State *L) {
|
||||
RanState *state = (RanState *)lua_newuserdatauv(L, sizeof(RanState), 0);
|
||||
randseed(L, state); /* initialize with a "random" seed */
|
||||
setseed(L, state->s, luaL_makeseed(L), 0); /* initialize with random seed */
|
||||
lua_pop(L, 2); /* remove pushed seeds */
|
||||
luaL_setfuncs(L, randfuncs, 1);
|
||||
}
|
||||
|
||||
86
lmem.c
86
lmem.c
@@ -22,25 +22,6 @@
|
||||
#include "lstate.h"
|
||||
|
||||
|
||||
#if defined(EMERGENCYGCTESTS)
|
||||
/*
|
||||
** First allocation will fail whenever not building initial state.
|
||||
** (This fail will trigger 'tryagain' and a full GC cycle at every
|
||||
** allocation.)
|
||||
*/
|
||||
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
|
||||
if (completestate(g) && ns > 0) /* frees never fail */
|
||||
return NULL; /* fail */
|
||||
else /* normal allocation */
|
||||
return (*g->frealloc)(g->ud, block, os, ns);
|
||||
}
|
||||
#else
|
||||
#define firsttry(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns))
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** About the realloc function:
|
||||
@@ -60,6 +41,43 @@ static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
** Macro to call the allocation function.
|
||||
*/
|
||||
#define callfrealloc(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns))
|
||||
|
||||
|
||||
/*
|
||||
** When an allocation fails, it will try again after an emergency
|
||||
** collection, except when it cannot run a collection. The GC should
|
||||
** not be called while the state is not fully built, as the collector
|
||||
** is not yet fully initialized. Also, it should not be called when
|
||||
** 'gcstopem' is true, because then the interpreter is in the middle of
|
||||
** a collection step.
|
||||
*/
|
||||
#define cantryagain(g) (completestate(g) && !g->gcstopem)
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(EMERGENCYGCTESTS)
|
||||
/*
|
||||
** First allocation will fail except when freeing a block (frees never
|
||||
** fail) and when it cannot try again; this fail will trigger 'tryagain'
|
||||
** and a full GC cycle at every allocation.
|
||||
*/
|
||||
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
|
||||
if (ns > 0 && cantryagain(g))
|
||||
return NULL; /* fail */
|
||||
else /* normal allocation */
|
||||
return callfrealloc(g, block, os, ns);
|
||||
}
|
||||
#else
|
||||
#define firsttry(g,block,os,ns) callfrealloc(g, block, os, ns)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
@@ -77,7 +95,7 @@ static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
|
||||
|
||||
|
||||
void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *psize,
|
||||
int size_elems, int limit, const char *what) {
|
||||
unsigned size_elems, int limit, const char *what) {
|
||||
void *newblock;
|
||||
int size = *psize;
|
||||
if (nelems + 1 <= size) /* does one extra element still fit? */
|
||||
@@ -108,10 +126,10 @@ void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *psize,
|
||||
** error.
|
||||
*/
|
||||
void *luaM_shrinkvector_ (lua_State *L, void *block, int *size,
|
||||
int final_n, int size_elem) {
|
||||
int final_n, unsigned size_elem) {
|
||||
void *newblock;
|
||||
size_t oldsize = cast_sizet((*size) * size_elem);
|
||||
size_t newsize = cast_sizet(final_n * size_elem);
|
||||
size_t oldsize = cast_sizet(*size) * size_elem;
|
||||
size_t newsize = cast_sizet(final_n) * size_elem;
|
||||
lua_assert(newsize <= oldsize);
|
||||
newblock = luaM_saferealloc_(L, block, oldsize, newsize);
|
||||
*size = final_n;
|
||||
@@ -132,27 +150,23 @@ 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((osize == 0) == (block == NULL));
|
||||
(*g->frealloc)(g->ud, block, osize, 0);
|
||||
g->GCdebt -= osize;
|
||||
callfrealloc(g, block, osize, 0);
|
||||
g->GCdebt += cast(l_mem, osize);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** In case of allocation fail, this function will do an emergency
|
||||
** collection to free some memory and then try the allocation again.
|
||||
** The GC should not be called while state is not fully built, as the
|
||||
** collector is not yet fully initialized. Also, it should not be called
|
||||
** when 'gcstopem' is true, because then the interpreter is in the
|
||||
** middle of a collection step.
|
||||
*/
|
||||
static void *tryagain (lua_State *L, void *block,
|
||||
size_t osize, size_t nsize) {
|
||||
global_State *g = G(L);
|
||||
if (completestate(g) && !g->gcstopem) {
|
||||
if (cantryagain(g)) {
|
||||
luaC_fullgc(L, 1); /* try to free some memory... */
|
||||
return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */
|
||||
return callfrealloc(g, block, osize, nsize); /* try again */
|
||||
}
|
||||
else return NULL; /* cannot free any memory without a full state */
|
||||
else return NULL; /* cannot run an emergency collection */
|
||||
}
|
||||
|
||||
|
||||
@@ -170,7 +184,7 @@ void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
|
||||
return NULL; /* do not update 'GCdebt' */
|
||||
}
|
||||
lua_assert((nsize == 0) == (newblock == NULL));
|
||||
g->GCdebt = (g->GCdebt + nsize) - osize;
|
||||
g->GCdebt -= cast(l_mem, nsize) - cast(l_mem, osize);
|
||||
return newblock;
|
||||
}
|
||||
|
||||
@@ -189,13 +203,13 @@ void *luaM_malloc_ (lua_State *L, size_t size, int tag) {
|
||||
return NULL; /* that's all */
|
||||
else {
|
||||
global_State *g = G(L);
|
||||
void *newblock = firsttry(g, NULL, tag, size);
|
||||
void *newblock = firsttry(g, NULL, cast_sizet(tag), size);
|
||||
if (l_unlikely(newblock == NULL)) {
|
||||
newblock = tryagain(L, NULL, tag, size);
|
||||
newblock = tryagain(L, NULL, cast_sizet(tag), size);
|
||||
if (newblock == NULL)
|
||||
luaM_error(L);
|
||||
}
|
||||
g->GCdebt += size;
|
||||
g->GCdebt -= cast(l_mem, size);
|
||||
return newblock;
|
||||
}
|
||||
}
|
||||
|
||||
13
lmem.h
13
lmem.h
@@ -39,11 +39,11 @@
|
||||
** Computes the minimum between 'n' and 'MAX_SIZET/sizeof(t)', so that
|
||||
** the result is not larger than 'n' and cannot overflow a 'size_t'
|
||||
** when multiplied by the size of type 't'. (Assumes that 'n' is an
|
||||
** 'int' or 'unsigned int' and that 'int' is not larger than 'size_t'.)
|
||||
** 'int' and that 'int' is not larger than 'size_t'.)
|
||||
*/
|
||||
#define luaM_limitN(n,t) \
|
||||
((cast_sizet(n) <= MAX_SIZET/sizeof(t)) ? (n) : \
|
||||
cast_uint((MAX_SIZET/sizeof(t))))
|
||||
cast_int((MAX_SIZET/sizeof(t))))
|
||||
|
||||
|
||||
/*
|
||||
@@ -57,12 +57,15 @@
|
||||
#define luaM_freearray(L, b, n) luaM_free_(L, (b), (n)*sizeof(*(b)))
|
||||
|
||||
#define luaM_new(L,t) cast(t*, luaM_malloc_(L, sizeof(t), 0))
|
||||
#define luaM_newvector(L,n,t) cast(t*, luaM_malloc_(L, (n)*sizeof(t), 0))
|
||||
#define luaM_newvector(L,n,t) \
|
||||
cast(t*, luaM_malloc_(L, cast_sizet(n)*sizeof(t), 0))
|
||||
#define luaM_newvectorchecked(L,n,t) \
|
||||
(luaM_checksize(L,n,sizeof(t)), luaM_newvector(L,n,t))
|
||||
|
||||
#define luaM_newobject(L,tag,s) luaM_malloc_(L, (s), tag)
|
||||
|
||||
#define luaM_newblock(L, size) luaM_newvector(L, size, char)
|
||||
|
||||
#define luaM_growvector(L,v,nelems,size,t,limit,e) \
|
||||
((v)=cast(t *, luaM_growaux_(L,v,nelems,&(size),sizeof(t), \
|
||||
luaM_limitN(limit,t),e)))
|
||||
@@ -83,10 +86,10 @@ LUAI_FUNC void *luaM_saferealloc_ (lua_State *L, void *block, size_t oldsize,
|
||||
size_t size);
|
||||
LUAI_FUNC void luaM_free_ (lua_State *L, void *block, size_t osize);
|
||||
LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int nelems,
|
||||
int *size, int size_elem, int limit,
|
||||
int *size, unsigned size_elem, int limit,
|
||||
const char *what);
|
||||
LUAI_FUNC void *luaM_shrinkvector_ (lua_State *L, void *block, int *nelem,
|
||||
int final_n, int size_elem);
|
||||
int final_n, unsigned size_elem);
|
||||
LUAI_FUNC void *luaM_malloc_ (lua_State *L, size_t size, int tag);
|
||||
|
||||
#endif
|
||||
|
||||
69
loadlib.c
69
loadlib.c
@@ -22,15 +22,7 @@
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
|
||||
/*
|
||||
** LUA_IGMARK is a mark to ignore all before it when building the
|
||||
** luaopen_ function name.
|
||||
*/
|
||||
#if !defined (LUA_IGMARK)
|
||||
#define LUA_IGMARK "-"
|
||||
#endif
|
||||
#include "llimits.h"
|
||||
|
||||
|
||||
/*
|
||||
@@ -67,11 +59,8 @@ static const char *const CLIBS = "_CLIBS";
|
||||
#define setprogdir(L) ((void)0)
|
||||
|
||||
|
||||
/*
|
||||
** Special type equivalent to '(void*)' for functions in gcc
|
||||
** (to suppress warnings when converting function pointers)
|
||||
*/
|
||||
typedef void (*voidf)(void);
|
||||
/* cast void* to a Lua function */
|
||||
#define cast_Lfunc(p) cast(lua_CFunction, cast_func(p))
|
||||
|
||||
|
||||
/*
|
||||
@@ -104,26 +93,13 @@ static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym);
|
||||
#if defined(LUA_USE_DLOPEN) /* { */
|
||||
/*
|
||||
** {========================================================================
|
||||
** This is an implementation of loadlib based on the dlfcn interface.
|
||||
** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD,
|
||||
** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least
|
||||
** as an emulation layer on top of native functions.
|
||||
** This is an implementation of loadlib based on the dlfcn interface,
|
||||
** which is available in all POSIX systems.
|
||||
** =========================================================================
|
||||
*/
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
/*
|
||||
** Macro to convert pointer-to-void* to pointer-to-function. This cast
|
||||
** is undefined according to ISO C, but POSIX assumes that it works.
|
||||
** (The '__extension__' in gnu compilers is only to avoid warnings.)
|
||||
*/
|
||||
#if defined(__GNUC__)
|
||||
#define cast_func(p) (__extension__ (lua_CFunction)(p))
|
||||
#else
|
||||
#define cast_func(p) ((lua_CFunction)(p))
|
||||
#endif
|
||||
|
||||
|
||||
static void lsys_unloadlib (void *lib) {
|
||||
dlclose(lib);
|
||||
@@ -139,7 +115,7 @@ static void *lsys_load (lua_State *L, const char *path, int seeglb) {
|
||||
|
||||
|
||||
static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) {
|
||||
lua_CFunction f = cast_func(dlsym(lib, sym));
|
||||
lua_CFunction f = cast_Lfunc(dlsym(lib, sym));
|
||||
if (l_unlikely(f == NULL))
|
||||
lua_pushstring(L, dlerror());
|
||||
return f;
|
||||
@@ -215,7 +191,7 @@ static void *lsys_load (lua_State *L, const char *path, int seeglb) {
|
||||
|
||||
|
||||
static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) {
|
||||
lua_CFunction f = (lua_CFunction)(voidf)GetProcAddress((HMODULE)lib, sym);
|
||||
lua_CFunction f = cast_Lfunc(GetProcAddress((HMODULE)lib, sym));
|
||||
if (f == NULL) pusherror(L);
|
||||
return f;
|
||||
}
|
||||
@@ -292,7 +268,8 @@ static int noenv (lua_State *L) {
|
||||
|
||||
|
||||
/*
|
||||
** Set a path
|
||||
** Set a path. (If using the default path, assume it is a string
|
||||
** literal in C and create it as an external string.)
|
||||
*/
|
||||
static void setpath (lua_State *L, const char *fieldname,
|
||||
const char *envname,
|
||||
@@ -303,7 +280,7 @@ static void setpath (lua_State *L, const char *fieldname,
|
||||
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 */
|
||||
lua_pushexternalstring(L, dft, strlen(dft), NULL, NULL); /* use default */
|
||||
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 */
|
||||
@@ -311,13 +288,13 @@ static void setpath (lua_State *L, const char *fieldname,
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
if (path < dftmark) { /* is there a prefix before ';;'? */
|
||||
luaL_addlstring(&b, path, dftmark - path); /* add it */
|
||||
luaL_addlstring(&b, path, ct_diff2sz(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_addlstring(&b, dftmark + 2, ct_diff2sz((path + len - 2) - dftmark));
|
||||
}
|
||||
luaL_pushresult(&b);
|
||||
}
|
||||
@@ -566,7 +543,7 @@ static int loadfunc (lua_State *L, const char *filename, const char *modname) {
|
||||
mark = strchr(modname, *LUA_IGMARK);
|
||||
if (mark) {
|
||||
int stat;
|
||||
openfunc = lua_pushlstring(L, modname, mark - modname);
|
||||
openfunc = lua_pushlstring(L, modname, ct_diff2sz(mark - modname));
|
||||
openfunc = lua_pushfstring(L, LUA_POF"%s", openfunc);
|
||||
stat = lookforfunc(L, filename, openfunc);
|
||||
if (stat != ERRFUNC) return stat;
|
||||
@@ -591,7 +568,7 @@ static int searcher_Croot (lua_State *L) {
|
||||
const char *p = strchr(name, '.');
|
||||
int stat;
|
||||
if (p == NULL) return 0; /* is root */
|
||||
lua_pushlstring(L, name, p - name);
|
||||
lua_pushlstring(L, name, ct_diff2sz(p - name));
|
||||
filename = findfile(L, lua_tostring(L, -1), "cpath", LUA_CSUBSEP);
|
||||
if (filename == NULL) return 1; /* root not found */
|
||||
if ((stat = loadfunc(L, filename, name)) != 0) {
|
||||
@@ -629,12 +606,12 @@ static void findloader (lua_State *L, const char *name) {
|
||||
!= LUA_TTABLE))
|
||||
luaL_error(L, "'package.searchers' must be a table");
|
||||
luaL_buffinit(L, &msg);
|
||||
luaL_addstring(&msg, "\n\t"); /* error-message prefix for first message */
|
||||
/* iterate over available searchers to find a loader */
|
||||
for (i = 1; ; i++) {
|
||||
luaL_addstring(&msg, "\n\t"); /* error-message prefix */
|
||||
if (l_unlikely(lua_rawgeti(L, 3, i) == LUA_TNIL)) { /* no more searchers? */
|
||||
lua_pop(L, 1); /* remove nil */
|
||||
luaL_buffsub(&msg, 2); /* remove prefix */
|
||||
luaL_buffsub(&msg, 2); /* remove last prefix */
|
||||
luaL_pushresult(&msg); /* create error message */
|
||||
luaL_error(L, "module '%s' not found:%s", name, lua_tostring(L, -1));
|
||||
}
|
||||
@@ -645,11 +622,10 @@ static void findloader (lua_State *L, const char *name) {
|
||||
else if (lua_isstring(L, -2)) { /* searcher returned error message? */
|
||||
lua_pop(L, 1); /* remove extra return */
|
||||
luaL_addvalue(&msg); /* concatenate error message */
|
||||
luaL_addstring(&msg, "\n\t"); /* prefix for next message */
|
||||
}
|
||||
else { /* no error message */
|
||||
else /* no error message */
|
||||
lua_pop(L, 2); /* remove both returns */
|
||||
luaL_buffsub(&msg, 2); /* remove prefix */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -708,8 +684,13 @@ static const luaL_Reg ll_funcs[] = {
|
||||
|
||||
|
||||
static void createsearcherstable (lua_State *L) {
|
||||
static const lua_CFunction searchers[] =
|
||||
{searcher_preload, searcher_Lua, searcher_C, searcher_Croot, NULL};
|
||||
static const lua_CFunction searchers[] = {
|
||||
searcher_preload,
|
||||
searcher_Lua,
|
||||
searcher_C,
|
||||
searcher_Croot,
|
||||
NULL
|
||||
};
|
||||
int i;
|
||||
/* create 'searchers' table */
|
||||
lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0);
|
||||
|
||||
341
lobject.c
341
lobject.c
@@ -10,6 +10,7 @@
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <float.h>
|
||||
#include <locale.h>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
@@ -32,8 +33,8 @@
|
||||
/*
|
||||
** Computes ceil(log2(x))
|
||||
*/
|
||||
int luaO_ceillog2 (unsigned int x) {
|
||||
static const lu_byte log_2[256] = { /* log_2[i] = ceil(log2(i - 1)) */
|
||||
lu_byte luaO_ceillog2 (unsigned int x) {
|
||||
static const lu_byte log_2[256] = { /* log_2[i - 1] = ceil(log2(i)) */
|
||||
0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
|
||||
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
@@ -46,7 +47,67 @@ int luaO_ceillog2 (unsigned int x) {
|
||||
int l = 0;
|
||||
x--;
|
||||
while (x >= 256) { l += 8; x >>= 8; }
|
||||
return l + log_2[x];
|
||||
return cast_byte(l + log_2[x]);
|
||||
}
|
||||
|
||||
/*
|
||||
** Encodes 'p'% as a floating-point byte, represented as (eeeexxxx).
|
||||
** The exponent is represented using excess-7. Mimicking IEEE 754, the
|
||||
** representation normalizes the number when possible, assuming an extra
|
||||
** 1 before the mantissa (xxxx) and adding one to the exponent (eeee)
|
||||
** to signal that. So, the real value is (1xxxx) * 2^(eeee - 7 - 1) if
|
||||
** eeee != 0, and (xxxx) * 2^-7 otherwise (subnormal numbers).
|
||||
*/
|
||||
lu_byte luaO_codeparam (unsigned int p) {
|
||||
if (p >= (cast(lu_mem, 0x1F) << (0xF - 7 - 1)) * 100u) /* overflow? */
|
||||
return 0xFF; /* return maximum value */
|
||||
else {
|
||||
p = (cast(l_uint32, p) * 128 + 99) / 100; /* round up the division */
|
||||
if (p < 0x10) { /* subnormal number? */
|
||||
/* exponent bits are already zero; nothing else to do */
|
||||
return cast_byte(p);
|
||||
}
|
||||
else { /* p >= 0x10 implies ceil(log2(p + 1)) >= 5 */
|
||||
/* preserve 5 bits in 'p' */
|
||||
unsigned log = luaO_ceillog2(p + 1) - 5u;
|
||||
return cast_byte(((p >> log) - 0x10) | ((log + 1) << 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Computes 'p' times 'x', where 'p' is a floating-point byte. Roughly,
|
||||
** we have to multiply 'x' by the mantissa and then shift accordingly to
|
||||
** the exponent. If the exponent is positive, both the multiplication
|
||||
** and the shift increase 'x', so we have to care only about overflows.
|
||||
** For negative exponents, however, multiplying before the shift keeps
|
||||
** more significant bits, as long as the multiplication does not
|
||||
** overflow, so we check which order is best.
|
||||
*/
|
||||
l_mem luaO_applyparam (lu_byte p, l_mem x) {
|
||||
unsigned int m = p & 0xF; /* mantissa */
|
||||
int e = (p >> 4); /* exponent */
|
||||
if (e > 0) { /* normalized? */
|
||||
e--; /* correct exponent */
|
||||
m += 0x10; /* correct mantissa; maximum value is 0x1F */
|
||||
}
|
||||
e -= 7; /* correct excess-7 */
|
||||
if (e >= 0) {
|
||||
if (x < (MAX_LMEM / 0x1F) >> e) /* no overflow? */
|
||||
return (x * m) << e; /* order doesn't matter here */
|
||||
else /* real overflow */
|
||||
return MAX_LMEM;
|
||||
}
|
||||
else { /* negative exponent */
|
||||
e = -e;
|
||||
if (x < MAX_LMEM / 0x1F) /* multiplication cannot overflow? */
|
||||
return (x * m) >> e; /* multiplying first gives more precision */
|
||||
else if ((x >> e) < MAX_LMEM / 0x1F) /* cannot overflow after shift? */
|
||||
return (x >> e) * m;
|
||||
else /* real overflow */
|
||||
return MAX_LMEM;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +123,7 @@ static lua_Integer intarith (lua_State *L, int op, lua_Integer v1,
|
||||
case LUA_OPBOR: return intop(|, v1, v2);
|
||||
case LUA_OPBXOR: return intop(^, v1, v2);
|
||||
case LUA_OPSHL: return luaV_shiftl(v1, v2);
|
||||
case LUA_OPSHR: return luaV_shiftl(v1, -v2);
|
||||
case LUA_OPSHR: return luaV_shiftr(v1, v2);
|
||||
case LUA_OPUNM: return intop(-, 0, v1);
|
||||
case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1);
|
||||
default: lua_assert(0); return 0;
|
||||
@@ -132,9 +193,10 @@ void luaO_arith (lua_State *L, int op, const TValue *p1, const TValue *p2,
|
||||
}
|
||||
|
||||
|
||||
int luaO_hexavalue (int c) {
|
||||
if (lisdigit(c)) return c - '0';
|
||||
else return (ltolower(c) - 'a') + 10;
|
||||
lu_byte luaO_hexavalue (int c) {
|
||||
lua_assert(lisxdigit(c));
|
||||
if (lisdigit(c)) return cast_byte(c - '0');
|
||||
else return cast_byte((ltolower(c) - 'a') + 10);
|
||||
}
|
||||
|
||||
|
||||
@@ -164,7 +226,7 @@ static int isneg (const char **s) {
|
||||
*/
|
||||
static lua_Number lua_strx2number (const char *s, char **endptr) {
|
||||
int dot = lua_getlocaledecpoint();
|
||||
lua_Number r = 0.0; /* result (accumulator) */
|
||||
lua_Number r = l_mathop(0.0); /* result (accumulator) */
|
||||
int sigdig = 0; /* number of significant digits */
|
||||
int nosigdig = 0; /* number of non-significant digits */
|
||||
int e = 0; /* exponent correction */
|
||||
@@ -174,7 +236,7 @@ static lua_Number lua_strx2number (const char *s, char **endptr) {
|
||||
while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */
|
||||
neg = isneg(&s); /* check sign */
|
||||
if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */
|
||||
return 0.0; /* invalid format (no '0x') */
|
||||
return l_mathop(0.0); /* invalid format (no '0x') */
|
||||
for (s += 2; ; s++) { /* skip '0x' and read numeral */
|
||||
if (*s == dot) {
|
||||
if (hasdot) break; /* second dot? stop loop */
|
||||
@@ -184,14 +246,14 @@ static lua_Number lua_strx2number (const char *s, char **endptr) {
|
||||
if (sigdig == 0 && *s == '0') /* non-significant digit (zero)? */
|
||||
nosigdig++;
|
||||
else if (++sigdig <= MAXSIGDIG) /* can read it without overflow? */
|
||||
r = (r * cast_num(16.0)) + luaO_hexavalue(*s);
|
||||
else e++; /* too many digits; ignore, but still count for exponent */
|
||||
r = (r * l_mathop(16.0)) + luaO_hexavalue(*s);
|
||||
else e++; /* too many digits; ignore, but still count for exponent */
|
||||
if (hasdot) e--; /* decimal digit? correct exponent */
|
||||
}
|
||||
else break; /* neither a dot nor a digit */
|
||||
}
|
||||
if (nosigdig + sigdig == 0) /* no digits? */
|
||||
return 0.0; /* invalid format */
|
||||
return l_mathop(0.0); /* invalid format */
|
||||
*endptr = cast_charp(s); /* valid up to here */
|
||||
e *= 4; /* each digit multiplies/divides value by 2^4 */
|
||||
if (*s == 'p' || *s == 'P') { /* exponent part? */
|
||||
@@ -200,7 +262,7 @@ static lua_Number lua_strx2number (const char *s, char **endptr) {
|
||||
s++; /* skip 'p' */
|
||||
neg1 = isneg(&s); /* sign */
|
||||
if (!lisdigit(cast_uchar(*s)))
|
||||
return 0.0; /* invalid; must have at least one digit */
|
||||
return l_mathop(0.0); /* invalid; must have at least one digit */
|
||||
while (lisdigit(cast_uchar(*s))) /* read exponent */
|
||||
exp1 = exp1 * 10 + *(s++) - '0';
|
||||
if (neg1) exp1 = -exp1;
|
||||
@@ -292,7 +354,7 @@ static const char *l_str2int (const char *s, lua_Integer *result) {
|
||||
int d = *s - '0';
|
||||
if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + neg)) /* overflow? */
|
||||
return NULL; /* do not accept it (as integer) */
|
||||
a = a * 10 + d;
|
||||
a = a * 10 + cast_uint(d);
|
||||
empty = 0;
|
||||
}
|
||||
}
|
||||
@@ -316,11 +378,11 @@ size_t luaO_str2num (const char *s, TValue *o) {
|
||||
}
|
||||
else
|
||||
return 0; /* conversion failed */
|
||||
return (e - s) + 1; /* success; return string size */
|
||||
return ct_diff2sz(e - s) + 1; /* success; return string size */
|
||||
}
|
||||
|
||||
|
||||
int luaO_utf8esc (char *buff, unsigned long x) {
|
||||
int luaO_utf8esc (char *buff, l_uint32 x) {
|
||||
int n = 1; /* number of bytes put in buffer (backwards) */
|
||||
lua_assert(x <= 0x7FFFFFFFu);
|
||||
if (x < 0x80) /* ascii? */
|
||||
@@ -339,32 +401,59 @@ int luaO_utf8esc (char *buff, unsigned long x) {
|
||||
|
||||
|
||||
/*
|
||||
** Maximum length of the conversion of a number to a string. Must be
|
||||
** enough to accommodate both LUA_INTEGER_FMT and LUA_NUMBER_FMT.
|
||||
** (For a long long int, this is 19 digits plus a sign and a final '\0',
|
||||
** adding to 21. For a long double, it can go to a sign, 33 digits,
|
||||
** the dot, an exponent letter, an exponent sign, 5 exponent digits,
|
||||
** and a final '\0', adding to 43.)
|
||||
** The size of the buffer for the conversion of a number to a string
|
||||
** 'LUA_N2SBUFFSZ' must be enough to accommodate both LUA_INTEGER_FMT
|
||||
** and LUA_NUMBER_FMT. For a long long int, this is 19 digits plus a
|
||||
** sign and a final '\0', adding to 21. For a long double, it can go to
|
||||
** a sign, the dot, an exponent letter, an exponent sign, 4 exponent
|
||||
** digits, the final '\0', plus the significant digits, which are
|
||||
** approximately the *_DIG attribute.
|
||||
*/
|
||||
#define MAXNUMBER2STR 44
|
||||
#if LUA_N2SBUFFSZ < (20 + l_floatatt(DIG))
|
||||
#error "invalid value for LUA_N2SBUFFSZ"
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Convert a number object to a string, adding it to a buffer
|
||||
** Convert a float to a string, adding it to a buffer. First try with
|
||||
** a not too large number of digits, to avoid noise (for instance,
|
||||
** 1.1 going to "1.1000000000000001"). If that lose precision, so
|
||||
** that reading the result back gives a different number, then do the
|
||||
** conversion again with extra precision. Moreover, if the numeral looks
|
||||
** like an integer (without a decimal point or an exponent), add ".0" to
|
||||
** its end.
|
||||
*/
|
||||
static int tostringbuff (TValue *obj, char *buff) {
|
||||
static int tostringbuffFloat (lua_Number n, char *buff) {
|
||||
/* first conversion */
|
||||
int len = l_sprintf(buff, LUA_N2SBUFFSZ, LUA_NUMBER_FMT,
|
||||
(LUAI_UACNUMBER)n);
|
||||
lua_Number check = lua_str2number(buff, NULL); /* read it back */
|
||||
if (check != n) { /* not enough precision? */
|
||||
/* convert again with more precision */
|
||||
len = l_sprintf(buff, LUA_N2SBUFFSZ, LUA_NUMBER_FMT_N,
|
||||
(LUAI_UACNUMBER)n);
|
||||
}
|
||||
/* looks like an integer? */
|
||||
if (buff[strspn(buff, "-0123456789")] == '\0') {
|
||||
buff[len++] = lua_getlocaledecpoint();
|
||||
buff[len++] = '0'; /* adds '.0' to result */
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a number object to a string, adding it to a buffer.
|
||||
*/
|
||||
unsigned luaO_tostringbuff (const TValue *obj, char *buff) {
|
||||
int len;
|
||||
lua_assert(ttisnumber(obj));
|
||||
if (ttisinteger(obj))
|
||||
len = lua_integer2str(buff, MAXNUMBER2STR, ivalue(obj));
|
||||
else {
|
||||
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 */
|
||||
}
|
||||
}
|
||||
return len;
|
||||
len = lua_integer2str(buff, LUA_N2SBUFFSZ, ivalue(obj));
|
||||
else
|
||||
len = tostringbuffFloat(fltvalue(obj), buff);
|
||||
lua_assert(len < LUA_N2SBUFFSZ);
|
||||
return cast_uint(len);
|
||||
}
|
||||
|
||||
|
||||
@@ -372,8 +461,8 @@ static int tostringbuff (TValue *obj, char *buff) {
|
||||
** 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);
|
||||
char buff[LUA_N2SBUFFSZ];
|
||||
unsigned len = luaO_tostringbuff(obj, buff);
|
||||
setsvalue(L, obj, luaS_newlstr(L, buff, len));
|
||||
}
|
||||
|
||||
@@ -386,80 +475,116 @@ void luaO_tostring (lua_State *L, TValue *obj) {
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/* size for buffer space used by 'luaO_pushvfstring' */
|
||||
#define BUFVFS 200
|
||||
/*
|
||||
** Size for buffer space used by 'luaO_pushvfstring'. It should be
|
||||
** (LUA_IDSIZE + LUA_N2SBUFFSZ) + a minimal space for basic messages,
|
||||
** so that 'luaG_addinfo' can work directly on the static buffer.
|
||||
*/
|
||||
#define BUFVFS cast_uint(LUA_IDSIZE + LUA_N2SBUFFSZ + 95)
|
||||
|
||||
/* buffer used by 'luaO_pushvfstring' */
|
||||
/*
|
||||
** Buffer used by 'luaO_pushvfstring'. 'err' signals an error while
|
||||
** building result (memory error [1] or buffer overflow [2]).
|
||||
*/
|
||||
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 */
|
||||
char *b;
|
||||
size_t buffsize;
|
||||
size_t blen; /* length of string in 'buff' */
|
||||
int err;
|
||||
char space[BUFVFS]; /* initial buffer */
|
||||
} BuffFS;
|
||||
|
||||
|
||||
static void initbuff (lua_State *L, BuffFS *buff) {
|
||||
buff->L = L;
|
||||
buff->b = buff->space;
|
||||
buff->buffsize = sizeof(buff->space);
|
||||
buff->blen = 0;
|
||||
buff->err = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Push given string to the stack, as part of the buffer, and
|
||||
** join the partial strings in the stack into one.
|
||||
** Push final result from 'luaO_pushvfstring'. This function may raise
|
||||
** errors explicitly or through memory errors, so it must run protected.
|
||||
*/
|
||||
static void pushstr (BuffFS *buff, const char *str, size_t l) {
|
||||
static void pushbuff (lua_State *L, void *ud) {
|
||||
BuffFS *buff = cast(BuffFS*, ud);
|
||||
switch (buff->err) {
|
||||
case 1: /* memory error */
|
||||
luaD_throw(L, LUA_ERRMEM);
|
||||
break;
|
||||
case 2: /* length overflow: Add "..." at the end of result */
|
||||
if (buff->buffsize - buff->blen < 3)
|
||||
strcpy(buff->b + buff->blen - 3, "..."); /* 'blen' must be > 3 */
|
||||
else { /* there is enough space left for the "..." */
|
||||
strcpy(buff->b + buff->blen, "...");
|
||||
buff->blen += 3;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
default: { /* no errors, but it can raise one creating the new string */
|
||||
TString *ts = luaS_newlstr(L, buff->b, buff->blen);
|
||||
setsvalue2s(L, L->top.p, ts);
|
||||
L->top.p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const char *clearbuff (BuffFS *buff) {
|
||||
lua_State *L = buff->L;
|
||||
setsvalue2s(L, L->top, luaS_newlstr(L, str, l));
|
||||
L->top++; /* may use one extra slot */
|
||||
buff->pushed++;
|
||||
luaV_concat(L, buff->pushed); /* join partial results into one */
|
||||
buff->pushed = 1;
|
||||
const char *res;
|
||||
if (luaD_rawrunprotected(L, pushbuff, buff) != LUA_OK) /* errors? */
|
||||
res = NULL; /* error message is on the top of the stack */
|
||||
else
|
||||
res = getstr(tsvalue(s2v(L->top.p - 1)));
|
||||
if (buff->b != buff->space) /* using dynamic buffer? */
|
||||
luaM_freearray(L, buff->b, buff->buffsize); /* free it */
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** 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 */
|
||||
size_t left = buff->buffsize - buff->blen; /* space left in the buffer */
|
||||
if (buff->err) /* do nothing else after an error */
|
||||
return;
|
||||
if (slen > left) { /* new string doesn't fit into current buffer? */
|
||||
if (slen > ((MAX_SIZE/2) - buff->blen)) { /* overflow? */
|
||||
memcpy(buff->b + buff->blen, str, left); /* copy what it can */
|
||||
buff->blen = buff->buffsize;
|
||||
buff->err = 2; /* doesn't add anything else */
|
||||
return;
|
||||
}
|
||||
else {
|
||||
size_t newsize = buff->buffsize + slen; /* limited to MAX_SIZE/2 */
|
||||
char *newb =
|
||||
(buff->b == buff->space) /* still using static space? */
|
||||
? luaM_reallocvector(buff->L, NULL, 0, newsize, char)
|
||||
: luaM_reallocvector(buff->L, buff->b, buff->buffsize, newsize,
|
||||
char);
|
||||
if (newb == NULL) { /* allocation error? */
|
||||
buff->err = 1; /* signal a memory error */
|
||||
return;
|
||||
}
|
||||
if (buff->b == buff->space) /* new buffer (not reallocated)? */
|
||||
memcpy(newb, buff->b, buff->blen); /* copy previous content */
|
||||
buff->b = newb; /* set new (larger) buffer... */
|
||||
buff->buffsize = newsize; /* ...and its new size */
|
||||
}
|
||||
}
|
||||
memcpy(buff->b + buff->blen, str, slen); /* copy new content */
|
||||
buff->blen += slen;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Add a number to the buffer.
|
||||
** Add a numeral 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);
|
||||
char numbuff[LUA_N2SBUFFSZ];
|
||||
unsigned len = luaO_tostringbuff(num, numbuff);
|
||||
addstr2buff(buff, numbuff, len);
|
||||
}
|
||||
|
||||
|
||||
@@ -470,10 +595,9 @@ static void addnum2buff (BuffFS *buff, TValue *num) {
|
||||
const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
|
||||
BuffFS buff; /* holds last part of the result */
|
||||
const char *e; /* points to next '%' */
|
||||
buff.pushed = buff.blen = 0;
|
||||
buff.L = L;
|
||||
initbuff(L, &buff);
|
||||
while ((e = strchr(fmt, '%')) != NULL) {
|
||||
addstr2buff(&buff, fmt, e - fmt); /* add 'fmt' up to '%' */
|
||||
addstr2buff(&buff, fmt, ct_diff2sz(e - fmt)); /* add 'fmt' up to '%' */
|
||||
switch (*(e + 1)) { /* conversion specifier */
|
||||
case 's': { /* zero-terminated string */
|
||||
const char *s = va_arg(argp, char *);
|
||||
@@ -482,7 +606,7 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
|
||||
break;
|
||||
}
|
||||
case 'c': { /* an 'int' as a character */
|
||||
char c = cast_uchar(va_arg(argp, int));
|
||||
char c = cast_char(va_arg(argp, int));
|
||||
addstr2buff(&buff, &c, sizeof(char));
|
||||
break;
|
||||
}
|
||||
@@ -494,7 +618,7 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
|
||||
}
|
||||
case 'I': { /* a 'lua_Integer' */
|
||||
TValue num;
|
||||
setivalue(&num, cast(lua_Integer, va_arg(argp, l_uacInt)));
|
||||
setivalue(&num, cast_Integer(va_arg(argp, l_uacInt)));
|
||||
addnum2buff(&buff, &num);
|
||||
break;
|
||||
}
|
||||
@@ -505,17 +629,17 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
|
||||
break;
|
||||
}
|
||||
case 'p': { /* a pointer */
|
||||
const int sz = 3 * sizeof(void*) + 8; /* enough space for '%p' */
|
||||
char *bf = getbuff(&buff, sz);
|
||||
char bf[LUA_N2SBUFFSZ]; /* enough space for '%p' */
|
||||
void *p = va_arg(argp, void *);
|
||||
int len = lua_pointer2str(bf, sz, p);
|
||||
addsize(&buff, len);
|
||||
int len = lua_pointer2str(bf, LUA_N2SBUFFSZ, p);
|
||||
addstr2buff(&buff, bf, cast_uint(len));
|
||||
break;
|
||||
}
|
||||
case 'U': { /* a 'long' as a UTF-8 sequence */
|
||||
case 'U': { /* an 'unsigned long' as a UTF-8 sequence */
|
||||
char bf[UTF8BUFFSZ];
|
||||
int len = luaO_utf8esc(bf, va_arg(argp, long));
|
||||
addstr2buff(&buff, bf + UTF8BUFFSZ - len, len);
|
||||
unsigned long arg = va_arg(argp, unsigned long);
|
||||
int len = luaO_utf8esc(bf, cast(l_uint32, arg));
|
||||
addstr2buff(&buff, bf + UTF8BUFFSZ - len, cast_uint(len));
|
||||
break;
|
||||
}
|
||||
case '%': {
|
||||
@@ -523,16 +647,14 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
luaG_runerror(L, "invalid option '%%%c' to 'lua_pushfstring'",
|
||||
*(e + 1));
|
||||
addstr2buff(&buff, e, 2); /* keep unknown format in the result */
|
||||
break;
|
||||
}
|
||||
}
|
||||
fmt = e + 2; /* skip '%' and the specifier */
|
||||
}
|
||||
addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */
|
||||
clearbuff(&buff); /* empty buffer into the stack */
|
||||
lua_assert(buff.pushed == 1);
|
||||
return svalue(s2v(L->top - 1));
|
||||
return clearbuff(&buff); /* empty buffer into a new string */
|
||||
}
|
||||
|
||||
|
||||
@@ -542,6 +664,8 @@ const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) {
|
||||
va_start(argp, fmt);
|
||||
msg = luaO_pushvfstring(L, fmt, argp);
|
||||
va_end(argp);
|
||||
if (msg == NULL) /* error? */
|
||||
luaD_throw(L, LUA_ERRMEM);
|
||||
return msg;
|
||||
}
|
||||
|
||||
@@ -581,7 +705,8 @@ void luaO_chunkid (char *out, const char *source, size_t srclen) {
|
||||
addstr(out, source, srclen); /* keep it */
|
||||
}
|
||||
else {
|
||||
if (nl != NULL) srclen = nl - source; /* stop at first newline */
|
||||
if (nl != NULL)
|
||||
srclen = ct_diff2sz(nl - source); /* stop at first newline */
|
||||
if (srclen > bufflen) srclen = bufflen;
|
||||
addstr(out, source, srclen);
|
||||
addstr(out, RETS, LL(RETS));
|
||||
|
||||
138
lobject.h
138
lobject.h
@@ -52,6 +52,8 @@ typedef union Value {
|
||||
lua_CFunction f; /* light C functions */
|
||||
lua_Integer i; /* integer numbers */
|
||||
lua_Number n; /* float numbers */
|
||||
/* not used, but may avoid warnings for uninitialized value */
|
||||
lu_byte ub;
|
||||
} Value;
|
||||
|
||||
|
||||
@@ -68,7 +70,7 @@ typedef struct TValue {
|
||||
|
||||
|
||||
#define val_(o) ((o)->value_)
|
||||
#define valraw(o) (&val_(o))
|
||||
#define valraw(o) (val_(o))
|
||||
|
||||
|
||||
/* raw type tag of a TValue */
|
||||
@@ -112,7 +114,7 @@ typedef struct TValue {
|
||||
#define settt_(o,t) ((o)->tt_=(t))
|
||||
|
||||
|
||||
/* main macro to copy values (from 'obj1' to 'obj2') */
|
||||
/* main macro to copy values (from 'obj2' to 'obj1') */
|
||||
#define setobj(L,obj1,obj2) \
|
||||
{ TValue *io1=(obj1); const TValue *io2=(obj2); \
|
||||
io1->value_ = io2->value_; settt_(io1, io2->tt_); \
|
||||
@@ -155,6 +157,17 @@ typedef union StackValue {
|
||||
/* index to stack elements */
|
||||
typedef StackValue *StkId;
|
||||
|
||||
|
||||
/*
|
||||
** When reallocating the stack, change all pointers to the stack into
|
||||
** proper offsets.
|
||||
*/
|
||||
typedef union {
|
||||
StkId p; /* actual pointer */
|
||||
ptrdiff_t offset; /* used while the stack is being reallocated */
|
||||
} StkIdRel;
|
||||
|
||||
|
||||
/* convert a 'StackValue' to a 'TValue' */
|
||||
#define s2v(o) (&(o)->val)
|
||||
|
||||
@@ -175,10 +188,21 @@ typedef StackValue *StkId;
|
||||
/* Value returned for a key not found in a table (absent key) */
|
||||
#define LUA_VABSTKEY makevariant(LUA_TNIL, 2)
|
||||
|
||||
/* Special variant to signal that a fast get is accessing a non-table */
|
||||
#define LUA_VNOTABLE makevariant(LUA_TNIL, 3)
|
||||
|
||||
|
||||
/* macro to test for (any kind of) nil */
|
||||
#define ttisnil(v) checktype((v), LUA_TNIL)
|
||||
|
||||
/*
|
||||
** Macro to test the result of a table access. Formally, it should
|
||||
** distinguish between LUA_VEMPTY/LUA_VABSTKEY/LUA_VNOTABLE and
|
||||
** other tags. As currently nil is equivalent to LUA_VEMPTY, it is
|
||||
** simpler to just test whether the value is nil.
|
||||
*/
|
||||
#define tagisempty(tag) (novariant(tag) == LUA_TNIL)
|
||||
|
||||
|
||||
/* macro to test for a standard nil */
|
||||
#define ttisstrictnil(o) checktag((o), LUA_VNIL)
|
||||
@@ -232,6 +256,8 @@ typedef StackValue *StkId;
|
||||
|
||||
|
||||
#define l_isfalse(o) (ttisfalse(o) || ttisnil(o))
|
||||
#define tagisfalse(t) ((t) == LUA_VFALSE || novariant(t) == LUA_TNIL)
|
||||
|
||||
|
||||
|
||||
#define setbfvalue(obj) settt_(obj, LUA_VFALSE)
|
||||
@@ -367,37 +393,53 @@ typedef struct GCObject {
|
||||
#define setsvalue2n setsvalue
|
||||
|
||||
|
||||
/* Kinds of long strings (stored in 'shrlen') */
|
||||
#define LSTRREG -1 /* regular long string */
|
||||
#define LSTRFIX -2 /* fixed external long string */
|
||||
#define LSTRMEM -3 /* external long string with deallocation */
|
||||
|
||||
|
||||
/*
|
||||
** Header for a string value.
|
||||
*/
|
||||
typedef struct TString {
|
||||
CommonHeader;
|
||||
lu_byte extra; /* reserved words for short strings; "has hash" for longs */
|
||||
lu_byte shrlen; /* length for short strings */
|
||||
ls_byte shrlen; /* length for short strings, negative for long strings */
|
||||
unsigned int hash;
|
||||
union {
|
||||
size_t lnglen; /* length for long strings */
|
||||
struct TString *hnext; /* linked list for hash table */
|
||||
} u;
|
||||
char contents[1];
|
||||
char *contents; /* pointer to content in long strings */
|
||||
lua_Alloc falloc; /* deallocation function for external strings */
|
||||
void *ud; /* user data for external strings */
|
||||
} TString;
|
||||
|
||||
|
||||
#define strisshr(ts) ((ts)->shrlen >= 0)
|
||||
|
||||
|
||||
/*
|
||||
** Get the actual string (array of bytes) from a 'TString'.
|
||||
** Get the actual string (array of bytes) from a 'TString'. (Generic
|
||||
** version and specialized versions for long and short strings.)
|
||||
*/
|
||||
#define getstr(ts) ((ts)->contents)
|
||||
#define rawgetshrstr(ts) (cast_charp(&(ts)->contents))
|
||||
#define getshrstr(ts) check_exp(strisshr(ts), rawgetshrstr(ts))
|
||||
#define getlngstr(ts) check_exp(!strisshr(ts), (ts)->contents)
|
||||
#define getstr(ts) (strisshr(ts) ? rawgetshrstr(ts) : (ts)->contents)
|
||||
|
||||
|
||||
/* get the actual string (array of bytes) from a Lua value */
|
||||
#define svalue(o) getstr(tsvalue(o))
|
||||
/* get string length from 'TString *ts' */
|
||||
#define tsslen(ts) \
|
||||
(strisshr(ts) ? cast_sizet((ts)->shrlen) : (ts)->u.lnglen)
|
||||
|
||||
/* get string length from 'TString *s' */
|
||||
#define tsslen(s) ((s)->tt == LUA_VSHRSTR ? (s)->shrlen : (s)->u.lnglen)
|
||||
|
||||
/* get string length from 'TValue *o' */
|
||||
#define vslen(o) tsslen(tsvalue(o))
|
||||
/*
|
||||
** Get string and length */
|
||||
#define getlstr(ts, len) \
|
||||
(strisshr(ts) \
|
||||
? (cast_void((len) = cast_sizet((ts)->shrlen)), rawgetshrstr(ts)) \
|
||||
: (cast_void((len) = (ts)->u.lnglen), (ts)->contents))
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
@@ -475,8 +517,8 @@ typedef struct Udata0 {
|
||||
|
||||
/* compute the offset of the memory area of a userdata */
|
||||
#define udatamemoffset(nuv) \
|
||||
((nuv) == 0 ? offsetof(Udata0, bindata) \
|
||||
: offsetof(Udata, uv) + (sizeof(UValue) * (nuv)))
|
||||
((nuv) == 0 ? offsetof(Udata0, bindata) \
|
||||
: offsetof(Udata, uv) + (sizeof(UValue) * (nuv)))
|
||||
|
||||
/* get the address of the memory block inside 'Udata' */
|
||||
#define getudatamem(u) (cast_charp(u) + udatamemoffset((u)->nuvalue))
|
||||
@@ -496,6 +538,9 @@ typedef struct Udata0 {
|
||||
#define LUA_VPROTO makevariant(LUA_TPROTO, 0)
|
||||
|
||||
|
||||
typedef l_uint32 Instruction;
|
||||
|
||||
|
||||
/*
|
||||
** Description of an upvalue for function prototypes
|
||||
*/
|
||||
@@ -533,13 +578,21 @@ typedef struct AbsLineInfo {
|
||||
int line;
|
||||
} AbsLineInfo;
|
||||
|
||||
|
||||
/*
|
||||
** Flags in Prototypes
|
||||
*/
|
||||
#define PF_ISVARARG 1
|
||||
#define PF_FIXED 2 /* prototype has parts in fixed memory */
|
||||
|
||||
|
||||
/*
|
||||
** Function Prototypes
|
||||
*/
|
||||
typedef struct Proto {
|
||||
CommonHeader;
|
||||
lu_byte numparams; /* number of fixed (named) parameters */
|
||||
lu_byte is_vararg;
|
||||
lu_byte flag;
|
||||
lu_byte maxstacksize; /* number of registers needed by this function */
|
||||
int sizeupvalues; /* size of 'upvalues' */
|
||||
int sizek; /* size of 'k' */
|
||||
@@ -615,8 +668,10 @@ 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 {
|
||||
TValue *p; /* points to stack or to its own value */
|
||||
ptrdiff_t offset; /* used while the stack is being reallocated */
|
||||
} v;
|
||||
union {
|
||||
struct { /* (when open) */
|
||||
struct UpVal *next; /* linked list */
|
||||
@@ -695,10 +750,9 @@ typedef union Node {
|
||||
|
||||
|
||||
/* copy a value into a key */
|
||||
#define setnodekey(L,node,obj) \
|
||||
#define setnodekey(node,obj) \
|
||||
{ Node *n_=(node); const TValue *io_=(obj); \
|
||||
n_->u.key_val = io_->value_; n_->u.key_tt = io_->tt_; \
|
||||
checkliveness(L,io_); }
|
||||
n_->u.key_val = io_->value_; n_->u.key_tt = io_->tt_; }
|
||||
|
||||
|
||||
/* copy a value from a key */
|
||||
@@ -708,27 +762,14 @@ typedef union Node {
|
||||
checkliveness(L,io_); }
|
||||
|
||||
|
||||
/*
|
||||
** About 'alimit': if 'isrealasize(t)' is true, then 'alimit' is the
|
||||
** real size of 'array'. Otherwise, the real size of 'array' is the
|
||||
** smallest power of two not smaller than 'alimit' (or zero iff 'alimit'
|
||||
** is zero); 'alimit' is then used as a hint for #t.
|
||||
*/
|
||||
|
||||
#define BITRAS (1 << 7)
|
||||
#define isrealasize(t) (!((t)->flags & BITRAS))
|
||||
#define setrealasize(t) ((t)->flags &= cast_byte(~BITRAS))
|
||||
#define setnorealasize(t) ((t)->flags |= BITRAS)
|
||||
|
||||
|
||||
typedef struct Table {
|
||||
CommonHeader;
|
||||
lu_byte flags; /* 1<<p means tagmethod(p) is not present */
|
||||
lu_byte lsizenode; /* log2 of size of 'node' array */
|
||||
unsigned int alimit; /* "limit" of 'array' array */
|
||||
TValue *array; /* array part */
|
||||
lu_byte lsizenode; /* log2 of number of slots of 'node' array */
|
||||
unsigned int asize; /* number of slots in 'array' array */
|
||||
Value *array; /* array part */
|
||||
Node *node;
|
||||
Node *lastfree; /* any free position is before this position */
|
||||
struct Table *metatable;
|
||||
GCObject *gclist;
|
||||
} Table;
|
||||
@@ -771,24 +812,37 @@ typedef struct Table {
|
||||
** 'module' operation for hashing (size is always a power of 2)
|
||||
*/
|
||||
#define lmod(s,size) \
|
||||
(check_exp((size&(size-1))==0, (cast_int((s) & ((size)-1)))))
|
||||
(check_exp((size&(size-1))==0, (cast_uint(s) & cast_uint((size)-1))))
|
||||
|
||||
|
||||
#define twoto(x) (1<<(x))
|
||||
#define twoto(x) (1u<<(x))
|
||||
#define sizenode(t) (twoto((t)->lsizenode))
|
||||
|
||||
|
||||
/* size of buffer for 'luaO_utf8esc' function */
|
||||
#define UTF8BUFFSZ 8
|
||||
|
||||
LUAI_FUNC int luaO_utf8esc (char *buff, unsigned long x);
|
||||
LUAI_FUNC int luaO_ceillog2 (unsigned int x);
|
||||
|
||||
/* macro to call 'luaO_pushvfstring' correctly */
|
||||
#define pushvfstring(L, argp, fmt, msg) \
|
||||
{ va_start(argp, fmt); \
|
||||
msg = luaO_pushvfstring(L, fmt, argp); \
|
||||
va_end(argp); \
|
||||
if (msg == NULL) luaD_throw(L, LUA_ERRMEM); /* only after 'va_end' */ }
|
||||
|
||||
|
||||
LUAI_FUNC int luaO_utf8esc (char *buff, l_uint32 x);
|
||||
LUAI_FUNC lu_byte luaO_ceillog2 (unsigned int x);
|
||||
LUAI_FUNC lu_byte luaO_codeparam (unsigned int p);
|
||||
LUAI_FUNC l_mem luaO_applyparam (lu_byte p, l_mem x);
|
||||
|
||||
LUAI_FUNC int luaO_rawarith (lua_State *L, int op, const TValue *p1,
|
||||
const TValue *p2, TValue *res);
|
||||
LUAI_FUNC void luaO_arith (lua_State *L, int op, const TValue *p1,
|
||||
const TValue *p2, StkId res);
|
||||
LUAI_FUNC size_t luaO_str2num (const char *s, TValue *o);
|
||||
LUAI_FUNC int luaO_hexavalue (int c);
|
||||
LUAI_FUNC unsigned luaO_tostringbuff (const TValue *obj, char *buff);
|
||||
LUAI_FUNC lu_byte luaO_hexavalue (int c);
|
||||
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);
|
||||
|
||||
42
lopcodes.c
42
lopcodes.c
@@ -13,6 +13,10 @@
|
||||
#include "lopcodes.h"
|
||||
|
||||
|
||||
#define opmode(mm,ot,it,t,a,m) \
|
||||
(((mm) << 7) | ((ot) << 6) | ((it) << 5) | ((t) << 4) | ((a) << 3) | (m))
|
||||
|
||||
|
||||
/* ORDER OP */
|
||||
|
||||
LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
|
||||
@@ -36,7 +40,7 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
|
||||
,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, ivABC) /* 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 */
|
||||
@@ -64,8 +68,8 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
|
||||
,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(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 */
|
||||
@@ -95,10 +99,40 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
|
||||
,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, 1, 0, 0, ivABC) /* OP_SETLIST */
|
||||
,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */
|
||||
,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */
|
||||
,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */
|
||||
,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Check whether instruction sets top for next instruction, that is,
|
||||
** it results in multiple values.
|
||||
*/
|
||||
int luaP_isOT (Instruction i) {
|
||||
OpCode op = GET_OPCODE(i);
|
||||
switch (op) {
|
||||
case OP_TAILCALL: return 1;
|
||||
default:
|
||||
return testOTMode(op) && GETARG_C(i) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check whether instruction uses top from previous instruction, that is,
|
||||
** it accepts multiple results.
|
||||
*/
|
||||
int luaP_isIT (Instruction i) {
|
||||
OpCode op = GET_OPCODE(i);
|
||||
switch (op) {
|
||||
case OP_SETLIST:
|
||||
return testITMode(GET_OPCODE(i)) && GETARG_vB(i) == 0;
|
||||
default:
|
||||
return testITMode(GET_OPCODE(i)) && GETARG_B(i) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
149
lopcodes.h
149
lopcodes.h
@@ -8,6 +8,7 @@
|
||||
#define lopcodes_h
|
||||
|
||||
#include "llimits.h"
|
||||
#include "lobject.h"
|
||||
|
||||
|
||||
/*===========================================================================
|
||||
@@ -18,25 +19,30 @@
|
||||
3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
|
||||
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||
iABC C(8) | B(8) |k| A(8) | Op(7) |
|
||||
ivABC vC(10) | vB(6) |k| A(8) | Op(7) |
|
||||
iABx Bx(17) | A(8) | Op(7) |
|
||||
iAsBx sBx (signed)(17) | A(8) | Op(7) |
|
||||
iAx Ax(25) | Op(7) |
|
||||
isJ sJ(25) | Op(7) |
|
||||
isJ sJ (signed)(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
|
||||
corresponding unsigned argument.
|
||||
('v' stands for "variant", 's' for "signed", 'x' for "extended".)
|
||||
A signed argument is represented in excess K: The represented value is
|
||||
the written unsigned value minus K, where K is half (rounded down) the
|
||||
maximum value for the corresponding unsigned argument.
|
||||
===========================================================================*/
|
||||
|
||||
|
||||
enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
|
||||
/* basic instruction formats */
|
||||
enum OpMode {iABC, ivABC, iABx, iAsBx, iAx, isJ};
|
||||
|
||||
|
||||
/*
|
||||
** size and position of opcode arguments.
|
||||
*/
|
||||
#define SIZE_C 8
|
||||
#define SIZE_vC 10
|
||||
#define SIZE_B 8
|
||||
#define SIZE_vB 6
|
||||
#define SIZE_Bx (SIZE_C + SIZE_B + 1)
|
||||
#define SIZE_A 8
|
||||
#define SIZE_Ax (SIZE_Bx + SIZE_A)
|
||||
@@ -49,7 +55,9 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
|
||||
#define POS_A (POS_OP + SIZE_OP)
|
||||
#define POS_k (POS_A + SIZE_A)
|
||||
#define POS_B (POS_k + 1)
|
||||
#define POS_vB (POS_k + 1)
|
||||
#define POS_C (POS_B + SIZE_B)
|
||||
#define POS_vC (POS_vB + SIZE_vB)
|
||||
|
||||
#define POS_Bx POS_k
|
||||
|
||||
@@ -64,14 +72,17 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
|
||||
** so they must fit in ints.
|
||||
*/
|
||||
|
||||
/* Check whether type 'int' has at least 'b' bits ('b' < 32) */
|
||||
#define L_INTHASBITS(b) ((UINT_MAX >> ((b) - 1)) >= 1)
|
||||
/*
|
||||
** Check whether type 'int' has at least 'b' + 1 bits.
|
||||
** 'b' < 32; +1 for the sign bit.
|
||||
*/
|
||||
#define L_INTHASBITS(b) ((UINT_MAX >> (b)) >= 1)
|
||||
|
||||
|
||||
#if L_INTHASBITS(SIZE_Bx)
|
||||
#define MAXARG_Bx ((1<<SIZE_Bx)-1)
|
||||
#else
|
||||
#define MAXARG_Bx MAX_INT
|
||||
#define MAXARG_Bx INT_MAX
|
||||
#endif
|
||||
|
||||
#define OFFSET_sBx (MAXARG_Bx>>1) /* 'sBx' is signed */
|
||||
@@ -80,13 +91,13 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
|
||||
#if L_INTHASBITS(SIZE_Ax)
|
||||
#define MAXARG_Ax ((1<<SIZE_Ax)-1)
|
||||
#else
|
||||
#define MAXARG_Ax MAX_INT
|
||||
#define MAXARG_Ax INT_MAX
|
||||
#endif
|
||||
|
||||
#if L_INTHASBITS(SIZE_sJ)
|
||||
#define MAXARG_sJ ((1 << SIZE_sJ) - 1)
|
||||
#else
|
||||
#define MAXARG_sJ MAX_INT
|
||||
#define MAXARG_sJ INT_MAX
|
||||
#endif
|
||||
|
||||
#define OFFSET_sJ (MAXARG_sJ >> 1)
|
||||
@@ -94,7 +105,9 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
|
||||
|
||||
#define MAXARG_A ((1<<SIZE_A)-1)
|
||||
#define MAXARG_B ((1<<SIZE_B)-1)
|
||||
#define MAXARG_vB ((1<<SIZE_vB)-1)
|
||||
#define MAXARG_C ((1<<SIZE_C)-1)
|
||||
#define MAXARG_vC ((1<<SIZE_vC)-1)
|
||||
#define OFFSET_sC (MAXARG_C >> 1)
|
||||
|
||||
#define int2sC(i) ((i) + OFFSET_sC)
|
||||
@@ -113,28 +126,36 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
|
||||
|
||||
#define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0)))
|
||||
#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \
|
||||
((cast(Instruction, o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))
|
||||
((cast_Inst(o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))
|
||||
|
||||
#define checkopm(i,m) (getOpMode(GET_OPCODE(i)) == m)
|
||||
|
||||
|
||||
#define getarg(i,pos,size) (cast_int(((i)>>(pos)) & MASK1(size,0)))
|
||||
#define setarg(i,v,pos,size) ((i) = (((i)&MASK0(size,pos)) | \
|
||||
((cast(Instruction, v)<<pos)&MASK1(size,pos))))
|
||||
((cast_Inst(v)<<pos)&MASK1(size,pos))))
|
||||
|
||||
#define GETARG_A(i) getarg(i, POS_A, SIZE_A)
|
||||
#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_B(i) \
|
||||
check_exp(checkopm(i, iABC), getarg(i, POS_B, SIZE_B))
|
||||
#define GETARG_vB(i) \
|
||||
check_exp(checkopm(i, ivABC), getarg(i, POS_vB, SIZE_vB))
|
||||
#define GETARG_sB(i) sC2int(GETARG_B(i))
|
||||
#define SETARG_B(i,v) setarg(i, v, POS_B, SIZE_B)
|
||||
#define SETARG_vB(i,v) setarg(i, v, POS_vB, SIZE_vB)
|
||||
|
||||
#define GETARG_C(i) check_exp(checkopm(i, iABC), getarg(i, POS_C, SIZE_C))
|
||||
#define GETARG_C(i) \
|
||||
check_exp(checkopm(i, iABC), getarg(i, POS_C, SIZE_C))
|
||||
#define GETARG_vC(i) \
|
||||
check_exp(checkopm(i, ivABC), getarg(i, POS_vC, SIZE_vC))
|
||||
#define GETARG_sC(i) sC2int(GETARG_C(i))
|
||||
#define SETARG_C(i,v) setarg(i, v, POS_C, SIZE_C)
|
||||
#define SETARG_vC(i,v) setarg(i, v, POS_vC, SIZE_vC)
|
||||
|
||||
#define TESTARG_k(i) check_exp(checkopm(i, iABC), (cast_int(((i) & (1u << POS_k)))))
|
||||
#define GETARG_k(i) check_exp(checkopm(i, iABC), getarg(i, POS_k, 1))
|
||||
#define TESTARG_k(i) (cast_int(((i) & (1u << POS_k))))
|
||||
#define GETARG_k(i) getarg(i, POS_k, 1)
|
||||
#define SETARG_k(i,v) setarg(i, v, POS_k, 1)
|
||||
|
||||
#define GETARG_Bx(i) check_exp(checkopm(i, iABx), getarg(i, POS_Bx, SIZE_Bx))
|
||||
@@ -153,22 +174,28 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
|
||||
setarg(i, cast_uint((j)+OFFSET_sJ), POS_sJ, SIZE_sJ)
|
||||
|
||||
|
||||
#define CREATE_ABCk(o,a,b,c,k) ((cast(Instruction, o)<<POS_OP) \
|
||||
| (cast(Instruction, a)<<POS_A) \
|
||||
| (cast(Instruction, b)<<POS_B) \
|
||||
| (cast(Instruction, c)<<POS_C) \
|
||||
| (cast(Instruction, k)<<POS_k))
|
||||
#define CREATE_ABCk(o,a,b,c,k) ((cast_Inst(o)<<POS_OP) \
|
||||
| (cast_Inst(a)<<POS_A) \
|
||||
| (cast_Inst(b)<<POS_B) \
|
||||
| (cast_Inst(c)<<POS_C) \
|
||||
| (cast_Inst(k)<<POS_k))
|
||||
|
||||
#define CREATE_ABx(o,a,bc) ((cast(Instruction, o)<<POS_OP) \
|
||||
| (cast(Instruction, a)<<POS_A) \
|
||||
| (cast(Instruction, bc)<<POS_Bx))
|
||||
#define CREATE_vABCk(o,a,b,c,k) ((cast_Inst(o)<<POS_OP) \
|
||||
| (cast_Inst(a)<<POS_A) \
|
||||
| (cast_Inst(b)<<POS_vB) \
|
||||
| (cast_Inst(c)<<POS_vC) \
|
||||
| (cast_Inst(k)<<POS_k))
|
||||
|
||||
#define CREATE_Ax(o,a) ((cast(Instruction, o)<<POS_OP) \
|
||||
| (cast(Instruction, a)<<POS_Ax))
|
||||
#define CREATE_ABx(o,a,bc) ((cast_Inst(o)<<POS_OP) \
|
||||
| (cast_Inst(a)<<POS_A) \
|
||||
| (cast_Inst(bc)<<POS_Bx))
|
||||
|
||||
#define CREATE_sJ(o,j,k) ((cast(Instruction, o) << POS_OP) \
|
||||
| (cast(Instruction, j) << POS_sJ) \
|
||||
| (cast(Instruction, k) << POS_k))
|
||||
#define CREATE_Ax(o,a) ((cast_Inst(o)<<POS_OP) \
|
||||
| (cast_Inst(a)<<POS_Ax))
|
||||
|
||||
#define CREATE_sJ(o,j,k) ((cast_Inst(o) << POS_OP) \
|
||||
| (cast_Inst(j) << POS_sJ) \
|
||||
| (cast_Inst(k) << POS_k))
|
||||
|
||||
|
||||
#if !defined(MAXINDEXRK) /* (for debugging only) */
|
||||
@@ -177,9 +204,16 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
|
||||
|
||||
|
||||
/*
|
||||
** invalid register that fits in 8 bits
|
||||
** Maximum size for the stack of a Lua function. It must fit in 8 bits.
|
||||
** The highest valid register is one less than this value.
|
||||
*/
|
||||
#define NO_REG MAXARG_A
|
||||
#define MAX_FSTACK MAXARG_A
|
||||
|
||||
/*
|
||||
** Invalid register (one more than last valid register).
|
||||
*/
|
||||
#define NO_REG MAX_FSTACK
|
||||
|
||||
|
||||
|
||||
/*
|
||||
@@ -190,7 +224,8 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
|
||||
|
||||
|
||||
/*
|
||||
** grep "ORDER OP" if you change these enums
|
||||
** Grep "ORDER OP" if you change these enums. Opcodes marked with a (*)
|
||||
** has extra descriptions in the notes after the enumeration.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
@@ -203,25 +238,25 @@ OP_LOADF,/* A sBx R[A] := (lua_Number)sBx */
|
||||
OP_LOADK,/* A Bx R[A] := K[Bx] */
|
||||
OP_LOADKX,/* A R[A] := K[extra arg] */
|
||||
OP_LOADFALSE,/* A R[A] := false */
|
||||
OP_LFALSESKIP,/*A R[A] := false; pc++ */
|
||||
OP_LFALSESKIP,/*A R[A] := false; pc++ (*) */
|
||||
OP_LOADTRUE,/* A R[A] := true */
|
||||
OP_LOADNIL,/* A B R[A], R[A+1], ..., R[A+B] := nil */
|
||||
OP_GETUPVAL,/* A B R[A] := UpValue[B] */
|
||||
OP_SETUPVAL,/* A B UpValue[B] := R[A] */
|
||||
|
||||
OP_GETTABUP,/* A B C R[A] := UpValue[B][K[C]:string] */
|
||||
OP_GETTABUP,/* A B C R[A] := UpValue[B][K[C]:shortstring] */
|
||||
OP_GETTABLE,/* A B C R[A] := R[B][R[C]] */
|
||||
OP_GETI,/* A B C R[A] := R[B][C] */
|
||||
OP_GETFIELD,/* A B C R[A] := R[B][K[C]:string] */
|
||||
OP_GETFIELD,/* A B C R[A] := R[B][K[C]:shortstring] */
|
||||
|
||||
OP_SETTABUP,/* A B C UpValue[A][K[B]:string] := RK(C) */
|
||||
OP_SETTABUP,/* A B C UpValue[A][K[B]:shortstring] := RK(C) */
|
||||
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_SETFIELD,/* A B C R[A][K[B]:shortstring] := RK(C) */
|
||||
|
||||
OP_NEWTABLE,/* A B C k R[A] := {} */
|
||||
OP_NEWTABLE,/* A vB vC k R[A] := {} */
|
||||
|
||||
OP_SELF,/* A B C R[A+1] := R[B]; R[A] := R[B][RK(C):string] */
|
||||
OP_SELF,/* A B C R[A+1] := R[B]; R[A] := R[B][K[C]:shortstring] */
|
||||
|
||||
OP_ADDI,/* A B sC R[A] := R[B] + sC */
|
||||
|
||||
@@ -254,7 +289,7 @@ OP_BXOR,/* A B C R[A] := R[B] ~ R[C] */
|
||||
OP_SHL,/* A B C R[A] := R[B] << R[C] */
|
||||
OP_SHR,/* A B C R[A] := R[B] >> R[C] */
|
||||
|
||||
OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] */
|
||||
OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] (*) */
|
||||
OP_MMBINI,/* A sB C k call C metamethod over R[A] and sB */
|
||||
OP_MMBINK,/* A B C k call C metamethod over R[A] and K[B] */
|
||||
|
||||
@@ -280,7 +315,7 @@ OP_GTI,/* A sB k if ((R[A] > sB) ~= k) then pc++ */
|
||||
OP_GEI,/* A sB k if ((R[A] >= sB) ~= k) then pc++ */
|
||||
|
||||
OP_TEST,/* A k if (not R[A] == k) then pc++ */
|
||||
OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] */
|
||||
OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] (*) */
|
||||
|
||||
OP_CALL,/* A B C R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1]) */
|
||||
OP_TAILCALL,/* A B C k return R[A](R[A+1], ... ,R[A+B-1]) */
|
||||
@@ -297,7 +332,7 @@ OP_TFORPREP,/* A Bx create upvalue for R[A + 3]; pc+=Bx */
|
||||
OP_TFORCALL,/* A C R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2]); */
|
||||
OP_TFORLOOP,/* A Bx if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx } */
|
||||
|
||||
OP_SETLIST,/* A B C k R[A][C+i] := R[A+i], 1 <= i <= B */
|
||||
OP_SETLIST,/* A vB vC k R[A][vC+i] := R[A+i], 1 <= i <= vB */
|
||||
|
||||
OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */
|
||||
|
||||
@@ -315,6 +350,18 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
|
||||
|
||||
/*===========================================================================
|
||||
Notes:
|
||||
|
||||
(*) Opcode OP_LFALSESKIP is used to convert a condition to a boolean
|
||||
value, in a code equivalent to (not cond ? false : true). (It
|
||||
produces false and skips the next instruction producing true.)
|
||||
|
||||
(*) Opcodes OP_MMBIN and variants follow each arithmetic and
|
||||
bitwise opcode. If the operation succeeds, it skips this next
|
||||
opcode. Otherwise, this opcode calls the corresponding metamethod.
|
||||
|
||||
(*) Opcode OP_TESTSET is used in short-circuit expressions that need
|
||||
both to jump and to produce a value, such as (a = b or c).
|
||||
|
||||
(*) 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'.
|
||||
@@ -331,9 +378,9 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
|
||||
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
|
||||
(*) In OP_NEWTABLE, vB 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.
|
||||
is vC. Otherwise, the array size is EXTRAARG _ vC.
|
||||
|
||||
(*) For comparisons, k specifies what condition the test should accept
|
||||
(true or false).
|
||||
@@ -374,19 +421,9 @@ LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];)
|
||||
#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) \
|
||||
((testOTMode(GET_OPCODE(i)) && GETARG_C(i) == 0) || \
|
||||
GET_OPCODE(i) == OP_TAILCALL)
|
||||
|
||||
/* "in top" (uses top from previous instruction) */
|
||||
#define isIT(i) (testITMode(GET_OPCODE(i)) && GETARG_B(i) == 0)
|
||||
LUAI_FUNC int luaP_isOT (Instruction i);
|
||||
LUAI_FUNC int luaP_isIT (Instruction i);
|
||||
|
||||
#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
|
||||
|
||||
46
loslib.c
46
loslib.c
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
#include "llimits.h"
|
||||
|
||||
|
||||
/*
|
||||
@@ -30,23 +31,14 @@
|
||||
*/
|
||||
#if !defined(LUA_STRFTIMEOPTIONS) /* { */
|
||||
|
||||
/* options for ANSI C 89 (only 1-char options) */
|
||||
#define L_STRFTIMEC89 "aAbBcdHIjmMpSUwWxXyYZ%"
|
||||
|
||||
/* options for ISO C 99 and POSIX */
|
||||
#define L_STRFTIMEC99 "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \
|
||||
"||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */
|
||||
|
||||
/* options for Windows */
|
||||
#define L_STRFTIMEWIN "aAbBcdHIjmMpSUwWxXyYzZ%" \
|
||||
"||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */
|
||||
|
||||
#if defined(LUA_USE_WINDOWS)
|
||||
#define LUA_STRFTIMEOPTIONS L_STRFTIMEWIN
|
||||
#elif defined(LUA_USE_C89)
|
||||
#define LUA_STRFTIMEOPTIONS L_STRFTIMEC89
|
||||
#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \
|
||||
"||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */
|
||||
#elif defined(LUA_USE_C89) /* ANSI C 89 (only 1-char options) */
|
||||
#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%"
|
||||
#else /* C99 specification */
|
||||
#define LUA_STRFTIMEOPTIONS L_STRFTIMEC99
|
||||
#define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \
|
||||
"||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */
|
||||
#endif
|
||||
|
||||
#endif /* } */
|
||||
@@ -138,12 +130,21 @@
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
#if !defined(l_system)
|
||||
#if defined(LUA_USE_IOS)
|
||||
/* Despite claiming to be ISO C, iOS does not implement 'system'. */
|
||||
#define l_system(cmd) ((cmd) == NULL ? 0 : -1)
|
||||
#else
|
||||
#define l_system(cmd) system(cmd) /* default definition */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
static int os_execute (lua_State *L) {
|
||||
const char *cmd = luaL_optstring(L, 1, NULL);
|
||||
int stat;
|
||||
errno = 0;
|
||||
stat = system(cmd);
|
||||
stat = l_system(cmd);
|
||||
if (cmd != NULL)
|
||||
return luaL_execresult(L, stat);
|
||||
else {
|
||||
@@ -155,6 +156,7 @@ static int os_execute (lua_State *L) {
|
||||
|
||||
static int os_remove (lua_State *L) {
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
errno = 0;
|
||||
return luaL_fileresult(L, remove(filename) == 0, filename);
|
||||
}
|
||||
|
||||
@@ -162,6 +164,7 @@ static int os_remove (lua_State *L) {
|
||||
static int os_rename (lua_State *L) {
|
||||
const char *fromname = luaL_checkstring(L, 1);
|
||||
const char *toname = luaL_checkstring(L, 2);
|
||||
errno = 0;
|
||||
return luaL_fileresult(L, rename(fromname, toname) == 0, NULL);
|
||||
}
|
||||
|
||||
@@ -260,9 +263,7 @@ static int getfield (lua_State *L, const char *key, int d, int delta) {
|
||||
res = d;
|
||||
}
|
||||
else {
|
||||
/* 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))
|
||||
if (!(res >= 0 ? res - delta <= INT_MAX : INT_MIN + delta <= res))
|
||||
return luaL_error(L, "field '%s' is out-of-bound", key);
|
||||
res -= delta;
|
||||
}
|
||||
@@ -272,9 +273,9 @@ static int getfield (lua_State *L, const char *key, int d, int delta) {
|
||||
|
||||
|
||||
static const char *checkoption (lua_State *L, const char *conv,
|
||||
ptrdiff_t convlen, char *buff) {
|
||||
size_t convlen, char *buff) {
|
||||
const char *option = LUA_STRFTIMEOPTIONS;
|
||||
int oplen = 1; /* length of options being checked */
|
||||
unsigned oplen = 1; /* length of options being checked */
|
||||
for (; *option != '\0' && oplen <= convlen; option += oplen) {
|
||||
if (*option == '|') /* next block? */
|
||||
oplen++; /* will check options with next length (+1) */
|
||||
@@ -332,7 +333,8 @@ static int os_date (lua_State *L) {
|
||||
size_t reslen;
|
||||
char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT);
|
||||
s++; /* skip '%' */
|
||||
s = checkoption(L, s, se - s, cc + 1); /* copy specifier to 'cc' */
|
||||
/* copy specifier to 'cc' */
|
||||
s = checkoption(L, s, ct_diff2sz(se - s), cc + 1);
|
||||
reslen = strftime(buff, SIZETIMEFMT, cc, stm);
|
||||
luaL_addsize(&b, reslen);
|
||||
}
|
||||
|
||||
50
lparser.h
50
lparser.h
@@ -32,26 +32,32 @@ typedef enum {
|
||||
VKFLT, /* floating constant; nval = numerical float value */
|
||||
VKINT, /* integer constant; ival = numerical integer value */
|
||||
VKSTR, /* string constant; strval = TString address;
|
||||
(string is fixed by the lexer) */
|
||||
(string is fixed by the scanner) */
|
||||
VNONRELOC, /* expression has its value in a fixed register;
|
||||
info = result register */
|
||||
VLOCAL, /* local variable; var.ridx = register index;
|
||||
var.vidx = relative index in 'actvar.arr' */
|
||||
VGLOBAL, /* global variable;
|
||||
info = relative index in 'actvar.arr' (or -1 for
|
||||
implicit declaration) */
|
||||
VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */
|
||||
VCONST, /* compile-time <const> variable;
|
||||
info = absolute index in 'actvar.arr' */
|
||||
VINDEXED, /* indexed variable;
|
||||
ind.t = table register;
|
||||
ind.idx = key's R index */
|
||||
ind.idx = key's R index;
|
||||
ind.ro = true if it represents a read-only global;
|
||||
ind.keystr = if key is a string, index in 'k' of that string;
|
||||
-1 if key is not a string */
|
||||
VINDEXUP, /* indexed upvalue;
|
||||
ind.t = table upvalue;
|
||||
ind.idx = key's K index */
|
||||
ind.idx = key's K index;
|
||||
ind.* as in VINDEXED */
|
||||
VINDEXI, /* indexed variable with constant integer;
|
||||
ind.t = table register;
|
||||
ind.idx = key's value */
|
||||
VINDEXSTR, /* indexed variable with literal string;
|
||||
ind.t = table register;
|
||||
ind.idx = key's K index */
|
||||
ind.idx = key's K index;
|
||||
ind.* as in VINDEXED */
|
||||
VJMP, /* expression is a test/comparison;
|
||||
info = pc of corresponding jump instruction */
|
||||
VRELOC, /* expression can put result in any register;
|
||||
@@ -75,10 +81,12 @@ typedef struct expdesc {
|
||||
struct { /* for indexed variables */
|
||||
short idx; /* index (R or "long" K) */
|
||||
lu_byte t; /* table (register or upvalue) */
|
||||
lu_byte ro; /* true if variable is read-only */
|
||||
int keystr; /* index in 'k' of string key, or -1 if not a string */
|
||||
} ind;
|
||||
struct { /* for local variables */
|
||||
lu_byte ridx; /* register holding the variable */
|
||||
unsigned short vidx; /* compiler index (in 'actvar.arr') */
|
||||
short vidx; /* index in 'actvar.arr' */
|
||||
} var;
|
||||
} u;
|
||||
int t; /* patch list of 'exit when true' */
|
||||
@@ -87,12 +95,21 @@ typedef struct expdesc {
|
||||
|
||||
|
||||
/* kinds of variables */
|
||||
#define VDKREG 0 /* regular */
|
||||
#define RDKCONST 1 /* constant */
|
||||
#define VDKREG 0 /* regular local */
|
||||
#define RDKCONST 1 /* local constant */
|
||||
#define RDKTOCLOSE 2 /* to-be-closed */
|
||||
#define RDKCTC 3 /* compile-time constant */
|
||||
#define RDKCTC 3 /* local compile-time constant */
|
||||
#define GDKREG 4 /* regular global */
|
||||
#define GDKCONST 5 /* global constant */
|
||||
|
||||
/* description of an active local variable */
|
||||
/* variables that live in registers */
|
||||
#define varinreg(v) ((v)->vd.kind <= RDKTOCLOSE)
|
||||
|
||||
/* test for global variables */
|
||||
#define varglobal(v) ((v)->vd.kind >= GDKREG)
|
||||
|
||||
|
||||
/* description of an active variable */
|
||||
typedef union Vardesc {
|
||||
struct {
|
||||
TValuefields; /* constant value (if it is a compile-time constant) */
|
||||
@@ -111,8 +128,8 @@ typedef struct Labeldesc {
|
||||
TString *name; /* label identifier */
|
||||
int pc; /* position in code */
|
||||
int line; /* line where it appeared */
|
||||
lu_byte nactvar; /* number of active variables in that position */
|
||||
lu_byte close; /* goto that escapes upvalues */
|
||||
short nactvar; /* number of active variables in that position */
|
||||
lu_byte close; /* true for goto that escapes upvalues */
|
||||
} Labeldesc;
|
||||
|
||||
|
||||
@@ -146,6 +163,7 @@ typedef struct FuncState {
|
||||
struct FuncState *prev; /* enclosing function */
|
||||
struct LexState *ls; /* lexical state */
|
||||
struct BlockCnt *bl; /* chain of current blocks */
|
||||
Table *kcache; /* cache for reusing constants */
|
||||
int pc; /* next position to code (equivalent to 'ncode') */
|
||||
int lasttarget; /* 'label' of last 'jump label' */
|
||||
int previousline; /* last line that was saved in 'lineinfo' */
|
||||
@@ -155,7 +173,7 @@ typedef struct FuncState {
|
||||
int firstlocal; /* index of first local var (in Dyndata array) */
|
||||
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 */
|
||||
short nactvar; /* number of active variable declarations */
|
||||
lu_byte nups; /* number of upvalues */
|
||||
lu_byte freereg; /* first free register */
|
||||
lu_byte iwthabs; /* instructions issued since last absolute line info */
|
||||
@@ -163,7 +181,9 @@ typedef struct FuncState {
|
||||
} FuncState;
|
||||
|
||||
|
||||
LUAI_FUNC int luaY_nvarstack (FuncState *fs);
|
||||
LUAI_FUNC lu_byte luaY_nvarstack (FuncState *fs);
|
||||
LUAI_FUNC void luaY_checklimit (FuncState *fs, int v, int l,
|
||||
const char *what);
|
||||
LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
|
||||
Dyndata *dyd, const char *name, int firstchar);
|
||||
|
||||
|
||||
239
lstate.c
239
lstate.c
@@ -29,79 +29,45 @@
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** thread state + extra space
|
||||
*/
|
||||
typedef struct LX {
|
||||
lu_byte extra_[LUA_EXTRASPACE];
|
||||
lua_State l;
|
||||
} LX;
|
||||
|
||||
|
||||
/*
|
||||
** Main thread combines a thread state and the global state
|
||||
*/
|
||||
typedef struct LG {
|
||||
LX l;
|
||||
global_State g;
|
||||
} LG;
|
||||
|
||||
|
||||
|
||||
#define fromstate(L) (cast(LX *, cast(lu_byte *, (L)) - offsetof(LX, l)))
|
||||
|
||||
|
||||
/*
|
||||
** A macro to create a "random" seed when a state is created;
|
||||
** the seed is used to randomize string hashes.
|
||||
** these macros allow user-specific actions when a thread is
|
||||
** created/deleted
|
||||
*/
|
||||
#if !defined(luai_makeseed)
|
||||
#if !defined(luai_userstateopen)
|
||||
#define luai_userstateopen(L) ((void)L)
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
#if !defined(luai_userstateclose)
|
||||
#define luai_userstateclose(L) ((void)L)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Compute an initial seed with some level of randomness.
|
||||
** Rely on Address Space Layout Randomization (if present) and
|
||||
** current time.
|
||||
*/
|
||||
#define addbuff(b,p,e) \
|
||||
{ size_t t = cast_sizet(e); \
|
||||
memcpy(b + p, &t, sizeof(t)); p += sizeof(t); }
|
||||
|
||||
static unsigned int luai_makeseed (lua_State *L) {
|
||||
char buff[3 * sizeof(size_t)];
|
||||
unsigned int h = cast_uint(time(NULL));
|
||||
int p = 0;
|
||||
addbuff(buff, p, L); /* heap variable */
|
||||
addbuff(buff, p, &h); /* local variable */
|
||||
addbuff(buff, p, &lua_newstate); /* public function */
|
||||
lua_assert(p == sizeof(buff));
|
||||
return luaS_hash(buff, p, h);
|
||||
}
|
||||
#if !defined(luai_userstatethread)
|
||||
#define luai_userstatethread(L,L1) ((void)L)
|
||||
#endif
|
||||
|
||||
#if !defined(luai_userstatefree)
|
||||
#define luai_userstatefree(L,L1) ((void)L)
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** set GCdebt to a new value keeping the value (totalbytes + GCdebt)
|
||||
** invariant (and avoiding underflows in 'totalbytes')
|
||||
** set GCdebt to a new value keeping the real number of allocated
|
||||
** objects (GCtotalobjs - GCdebt) invariant and avoiding overflows in
|
||||
** 'GCtotalobjs'.
|
||||
*/
|
||||
void luaE_setdebt (global_State *g, l_mem debt) {
|
||||
l_mem tb = gettotalbytes(g);
|
||||
lua_assert(tb > 0);
|
||||
if (debt < tb - MAX_LMEM)
|
||||
debt = tb - MAX_LMEM; /* will make 'totalbytes == MAX_LMEM' */
|
||||
g->totalbytes = tb - debt;
|
||||
if (debt > MAX_LMEM - tb)
|
||||
debt = MAX_LMEM - tb; /* will make GCtotalbytes == MAX_LMEM */
|
||||
g->GCtotalbytes = tb + debt;
|
||||
g->GCdebt = debt;
|
||||
}
|
||||
|
||||
|
||||
LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) {
|
||||
UNUSED(L); UNUSED(limit);
|
||||
return LUAI_MAXCCALLS; /* warning?? */
|
||||
}
|
||||
|
||||
|
||||
CallInfo *luaE_extendCI (lua_State *L) {
|
||||
CallInfo *ci;
|
||||
lua_assert(L->ci->next == NULL);
|
||||
@@ -119,7 +85,7 @@ CallInfo *luaE_extendCI (lua_State *L) {
|
||||
/*
|
||||
** free all CallInfo structures not in use by a thread
|
||||
*/
|
||||
void luaE_freeCI (lua_State *L) {
|
||||
static void freeCI (lua_State *L) {
|
||||
CallInfo *ci = L->ci;
|
||||
CallInfo *next = ci->next;
|
||||
ci->next = NULL;
|
||||
@@ -166,7 +132,7 @@ void luaE_checkcstack (lua_State *L) {
|
||||
if (getCcalls(L) == LUAI_MAXCCALLS)
|
||||
luaG_runerror(L, "C stack overflow");
|
||||
else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11))
|
||||
luaD_throw(L, LUA_ERRERR); /* error while handing stack error */
|
||||
luaD_errerr(L); /* error while handling stack error */
|
||||
}
|
||||
|
||||
|
||||
@@ -177,36 +143,40 @@ LUAI_FUNC void luaE_incCstack (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
static void stack_init (lua_State *L1, lua_State *L) {
|
||||
int i; CallInfo *ci;
|
||||
/* initialize stack array */
|
||||
L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
|
||||
L1->tbclist = L1->stack;
|
||||
for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
|
||||
setnilvalue(s2v(L1->stack + i)); /* erase new stack */
|
||||
L1->top = L1->stack;
|
||||
L1->stack_last = L1->stack + BASIC_STACK_SIZE;
|
||||
/* initialize first ci */
|
||||
ci = &L1->base_ci;
|
||||
ci->next = ci->previous = NULL;
|
||||
ci->callstatus = CIST_C;
|
||||
ci->func = L1->top;
|
||||
static void resetCI (lua_State *L) {
|
||||
CallInfo *ci = L->ci = &L->base_ci;
|
||||
ci->func.p = L->stack.p;
|
||||
setnilvalue(s2v(ci->func.p)); /* 'function' entry for basic 'ci' */
|
||||
ci->top.p = ci->func.p + 1 + LUA_MINSTACK; /* +1 for 'function' entry */
|
||||
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;
|
||||
L1->ci = ci;
|
||||
ci->callstatus = CIST_C;
|
||||
L->status = LUA_OK;
|
||||
L->errfunc = 0; /* stack unwind can "throw away" the error function */
|
||||
}
|
||||
|
||||
|
||||
static void stack_init (lua_State *L1, lua_State *L) {
|
||||
int i;
|
||||
/* initialize stack array */
|
||||
L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
|
||||
L1->tbclist.p = L1->stack.p;
|
||||
for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
|
||||
setnilvalue(s2v(L1->stack.p + i)); /* erase new stack */
|
||||
L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE;
|
||||
/* initialize first ci */
|
||||
resetCI(L1);
|
||||
L1->top.p = L1->stack.p + 1; /* +1 for 'function' entry */
|
||||
}
|
||||
|
||||
|
||||
static void freestack (lua_State *L) {
|
||||
if (L->stack == NULL)
|
||||
if (L->stack.p == NULL)
|
||||
return; /* stack not completely built yet */
|
||||
L->ci = &L->base_ci; /* free the entire 'ci' list */
|
||||
luaE_freeCI(L);
|
||||
freeCI(L);
|
||||
lua_assert(L->nci == 0);
|
||||
luaM_freearray(L, L->stack, stacksize(L) + EXTRA_STACK); /* free stack */
|
||||
/* free stack */
|
||||
luaM_freearray(L, L->stack.p, cast_sizet(stacksize(L) + EXTRA_STACK));
|
||||
}
|
||||
|
||||
|
||||
@@ -215,13 +185,19 @@ static void freestack (lua_State *L) {
|
||||
*/
|
||||
static void init_registry (lua_State *L, global_State *g) {
|
||||
/* create registry */
|
||||
TValue aux;
|
||||
Table *registry = luaH_new(L);
|
||||
sethvalue(L, &g->l_registry, registry);
|
||||
luaH_resize(L, registry, LUA_RIDX_LAST, 0);
|
||||
/* registry[1] = false */
|
||||
setbfvalue(&aux);
|
||||
luaH_setint(L, registry, 1, &aux);
|
||||
/* registry[LUA_RIDX_MAINTHREAD] = L */
|
||||
setthvalue(L, ®istry->array[LUA_RIDX_MAINTHREAD - 1], L);
|
||||
setthvalue(L, &aux, L);
|
||||
luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &aux);
|
||||
/* registry[LUA_RIDX_GLOBALS] = new table (table of globals) */
|
||||
sethvalue(L, ®istry->array[LUA_RIDX_GLOBALS - 1], luaH_new(L));
|
||||
sethvalue(L, &aux, luaH_new(L));
|
||||
luaH_setint(L, registry, LUA_RIDX_GLOBALS, &aux);
|
||||
}
|
||||
|
||||
|
||||
@@ -236,7 +212,7 @@ static void f_luaopen (lua_State *L, void *ud) {
|
||||
luaS_init(L);
|
||||
luaT_init(L);
|
||||
luaX_init(L);
|
||||
g->gcrunning = 1; /* allow gc */
|
||||
g->gcstp = 0; /* allow gc */
|
||||
setnilvalue(&g->nilvalue); /* now state is complete */
|
||||
luai_userstateopen(L);
|
||||
}
|
||||
@@ -248,7 +224,7 @@ static void f_luaopen (lua_State *L, void *ud) {
|
||||
*/
|
||||
static void preinit_thread (lua_State *L, global_State *g) {
|
||||
G(L) = g;
|
||||
L->stack = NULL;
|
||||
L->stack.p = NULL;
|
||||
L->ci = NULL;
|
||||
L->nci = 0;
|
||||
L->twups = L; /* thread has no upvalues */
|
||||
@@ -263,40 +239,48 @@ static void preinit_thread (lua_State *L, global_State *g) {
|
||||
L->status = LUA_OK;
|
||||
L->errfunc = 0;
|
||||
L->oldpc = 0;
|
||||
L->base_ci.previous = L->base_ci.next = NULL;
|
||||
}
|
||||
|
||||
|
||||
lu_mem luaE_threadsize (lua_State *L) {
|
||||
lu_mem sz = cast(lu_mem, sizeof(LX))
|
||||
+ cast_uint(L->nci) * sizeof(CallInfo);
|
||||
if (L->stack.p != NULL)
|
||||
sz += cast_uint(stacksize(L) + EXTRA_STACK) * sizeof(StackValue);
|
||||
return sz;
|
||||
}
|
||||
|
||||
|
||||
static void close_state (lua_State *L) {
|
||||
global_State *g = G(L);
|
||||
if (!completestate(g)) /* closing a partially built state? */
|
||||
luaC_freeallobjects(L); /* jucst collect its objects */
|
||||
luaC_freeallobjects(L); /* just collect its objects */
|
||||
else { /* closing a fully built state */
|
||||
resetCI(L);
|
||||
luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */
|
||||
L->top.p = L->stack.p + 1; /* empty the stack to run finalizers */
|
||||
luaC_freeallobjects(L); /* collect all objects */
|
||||
luai_userstateclose(L);
|
||||
}
|
||||
luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size);
|
||||
luaM_freearray(L, G(L)->strt.hash, cast_sizet(G(L)->strt.size));
|
||||
freestack(L);
|
||||
lua_assert(gettotalbytes(g) == sizeof(LG));
|
||||
(*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */
|
||||
lua_assert(gettotalbytes(g) == sizeof(global_State));
|
||||
(*g->frealloc)(g->ud, g, sizeof(global_State), 0); /* free main block */
|
||||
}
|
||||
|
||||
|
||||
LUA_API lua_State *lua_newthread (lua_State *L) {
|
||||
global_State *g;
|
||||
global_State *g = G(L);
|
||||
GCObject *o;
|
||||
lua_State *L1;
|
||||
lua_lock(L);
|
||||
g = G(L);
|
||||
luaC_checkGC(L);
|
||||
/* create new thread */
|
||||
L1 = &cast(LX *, luaM_newobject(L, LUA_TTHREAD, sizeof(LX)))->l;
|
||||
L1->marked = luaC_white(g);
|
||||
L1->tt = LUA_VTHREAD;
|
||||
/* link it on list 'allgc' */
|
||||
L1->next = g->allgc;
|
||||
g->allgc = obj2gco(L1);
|
||||
o = luaC_newobjdt(L, LUA_TTHREAD, sizeof(LX), offsetof(LX, l));
|
||||
L1 = gco2th(o);
|
||||
/* anchor it on L stack */
|
||||
setthvalue2s(L, L->top, L1);
|
||||
setthvalue2s(L, L->top.p, L1);
|
||||
api_incr_top(L);
|
||||
preinit_thread(L1, g);
|
||||
L1->hookmask = L->hookmask;
|
||||
@@ -304,7 +288,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
|
||||
L1->hook = L->hook;
|
||||
resethookcount(L1);
|
||||
/* initialize L1 extra space */
|
||||
memcpy(lua_getextraspace(L1), lua_getextraspace(g->mainthread),
|
||||
memcpy(lua_getextraspace(L1), lua_getextraspace(mainthread(g)),
|
||||
LUA_EXTRASPACE);
|
||||
luai_userstatethread(L, L1);
|
||||
stack_init(L1, L); /* init stack */
|
||||
@@ -315,7 +299,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
|
||||
|
||||
void luaE_freethread (lua_State *L, lua_State *L1) {
|
||||
LX *l = fromstate(L1);
|
||||
luaF_closeupval(L1, L1->stack); /* close all upvalues */
|
||||
luaF_closeupval(L1, L1->stack.p); /* close all upvalues */
|
||||
lua_assert(L1->openupval == NULL);
|
||||
luai_userstatefree(L, L1);
|
||||
freestack(L1);
|
||||
@@ -323,42 +307,39 @@ void luaE_freethread (lua_State *L, lua_State *L1) {
|
||||
}
|
||||
|
||||
|
||||
int luaE_resetthread (lua_State *L, int status) {
|
||||
CallInfo *ci = L->ci = &L->base_ci; /* unwind CallInfo list */
|
||||
setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */
|
||||
ci->func = L->stack;
|
||||
ci->callstatus = CIST_C;
|
||||
TStatus luaE_resetthread (lua_State *L, TStatus status) {
|
||||
resetCI(L);
|
||||
if (status == LUA_YIELD)
|
||||
status = LUA_OK;
|
||||
status = luaD_closeprotected(L, 1, status);
|
||||
if (status != LUA_OK) /* errors? */
|
||||
luaD_seterrorobj(L, status, L->stack + 1);
|
||||
luaD_seterrorobj(L, status, L->stack.p + 1);
|
||||
else
|
||||
L->top = L->stack + 1;
|
||||
ci->top = L->top + LUA_MINSTACK;
|
||||
L->status = cast_byte(status);
|
||||
luaD_reallocstack(L, cast_int(ci->top - L->stack), 0);
|
||||
L->top.p = L->stack.p + 1;
|
||||
luaD_reallocstack(L, cast_int(L->ci->top.p - L->stack.p), 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
LUA_API int lua_resetthread (lua_State *L) {
|
||||
int status;
|
||||
LUA_API int lua_closethread (lua_State *L, lua_State *from) {
|
||||
TStatus status;
|
||||
lua_lock(L);
|
||||
L->nCcalls = (from) ? getCcalls(from) : 0;
|
||||
status = luaE_resetthread(L, L->status);
|
||||
if (L == from) /* closing itself? */
|
||||
luaD_throwbaselevel(L, status);
|
||||
lua_unlock(L);
|
||||
return status;
|
||||
return APIstatus(status);
|
||||
}
|
||||
|
||||
|
||||
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
|
||||
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud, unsigned seed) {
|
||||
int i;
|
||||
lua_State *L;
|
||||
global_State *g;
|
||||
LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG)));
|
||||
if (l == NULL) return NULL;
|
||||
L = &l->l.l;
|
||||
g = &l->g;
|
||||
global_State *g = cast(global_State*,
|
||||
(*f)(ud, NULL, LUA_TTHREAD, sizeof(global_State)));
|
||||
if (g == NULL) return NULL;
|
||||
L = &g->mainth.l;
|
||||
L->tt = LUA_VTHREAD;
|
||||
g->currentwhite = bitmask(WHITE0BIT);
|
||||
L->marked = luaC_white(g);
|
||||
@@ -370,9 +351,8 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
|
||||
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 */
|
||||
g->seed = seed;
|
||||
g->gcstp = GCSTPGC; /* no GC while building state */
|
||||
g->strt.size = g->strt.nuse = 0;
|
||||
g->strt.hash = NULL;
|
||||
setnilvalue(&g->l_registry);
|
||||
@@ -388,16 +368,17 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
|
||||
g->gray = g->grayagain = NULL;
|
||||
g->weak = g->ephemeron = g->allweak = NULL;
|
||||
g->twups = NULL;
|
||||
g->totalbytes = sizeof(LG);
|
||||
g->GCtotalbytes = sizeof(global_State);
|
||||
g->GCmarked = 0;
|
||||
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);
|
||||
g->gcstepsize = LUAI_GCSTEPSIZE;
|
||||
setgcparam(g->genmajormul, LUAI_GENMAJORMUL);
|
||||
g->genminormul = LUAI_GENMINORMUL;
|
||||
for (i=0; i < LUA_NUMTAGS; i++) g->mt[i] = NULL;
|
||||
setgcparam(g, PAUSE, LUAI_GCPAUSE);
|
||||
setgcparam(g, STEPMUL, LUAI_GCMUL);
|
||||
setgcparam(g, STEPSIZE, LUAI_GCSTEPSIZE);
|
||||
setgcparam(g, MINORMUL, LUAI_GENMINORMUL);
|
||||
setgcparam(g, MINORMAJOR, LUAI_MINORMAJOR);
|
||||
setgcparam(g, MAJORMINOR, LUAI_MAJORMINOR);
|
||||
for (i=0; i < LUA_NUMTYPES; i++) g->mt[i] = NULL;
|
||||
if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) {
|
||||
/* memory allocation error: free partial state */
|
||||
close_state(L);
|
||||
@@ -409,7 +390,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
|
||||
|
||||
LUA_API void lua_close (lua_State *L) {
|
||||
lua_lock(L);
|
||||
L = G(L)->mainthread; /* only the main thread can be closed */
|
||||
L = mainthread(G(L)); /* only the main thread can be closed */
|
||||
close_state(L);
|
||||
}
|
||||
|
||||
@@ -425,9 +406,9 @@ void luaE_warning (lua_State *L, const char *msg, int tocont) {
|
||||
** Generate a warning from an error message
|
||||
*/
|
||||
void luaE_warnerror (lua_State *L, const char *where) {
|
||||
TValue *errobj = s2v(L->top - 1); /* error object */
|
||||
TValue *errobj = s2v(L->top.p - 1); /* error object */
|
||||
const char *msg = (ttisstring(errobj))
|
||||
? svalue(errobj)
|
||||
? getstr(tsvalue(errobj))
|
||||
: "error object is not a string";
|
||||
/* produce warning "error in %s (%s)" (where, msg) */
|
||||
luaE_warning(L, "error in ", 1);
|
||||
|
||||
213
lstate.h
213
lstate.h
@@ -9,6 +9,11 @@
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
/* Some header files included here need this definition */
|
||||
typedef struct CallInfo CallInfo;
|
||||
|
||||
|
||||
#include "lobject.h"
|
||||
#include "ltm.h"
|
||||
#include "lzio.h"
|
||||
@@ -137,20 +142,32 @@ struct lua_longjmp; /* defined in ldo.c */
|
||||
#define EXTRA_STACK 5
|
||||
|
||||
|
||||
/*
|
||||
** Size of cache for strings in the API. 'N' is the number of
|
||||
** sets (better be a prime) and "M" is the size of each set.
|
||||
** (M == 1 makes a direct cache.)
|
||||
*/
|
||||
#if !defined(STRCACHE_N)
|
||||
#define STRCACHE_N 53
|
||||
#define STRCACHE_M 2
|
||||
#endif
|
||||
|
||||
|
||||
#define BASIC_STACK_SIZE (2*LUA_MINSTACK)
|
||||
|
||||
#define stacksize(th) cast_int((th)->stack_last - (th)->stack)
|
||||
#define stacksize(th) cast_int((th)->stack_last.p - (th)->stack.p)
|
||||
|
||||
|
||||
/* kinds of Garbage Collection */
|
||||
#define KGC_INC 0 /* incremental gc */
|
||||
#define KGC_GEN 1 /* generational gc */
|
||||
#define KGC_GENMINOR 1 /* generational gc in minor (regular) mode */
|
||||
#define KGC_GENMAJOR 2 /* generational in major mode */
|
||||
|
||||
|
||||
typedef struct stringtable {
|
||||
TString **hash;
|
||||
TString **hash; /* array of buckets (linked lists of strings) */
|
||||
int nuse; /* number of elements */
|
||||
int size;
|
||||
int size; /* number of buckets */
|
||||
} stringtable;
|
||||
|
||||
|
||||
@@ -165,18 +182,16 @@ typedef struct stringtable {
|
||||
** - field 'nyield' is used only while a function is "doing" an
|
||||
** yield (from the yield until the next resume);
|
||||
** - field 'nres' is used only while closing tbc variables when
|
||||
** returning from a C function;
|
||||
** - field 'transferinfo' is used only during call/returnhooks,
|
||||
** before the function starts or after it ends.
|
||||
** returning from a function;
|
||||
*/
|
||||
typedef struct CallInfo {
|
||||
StkId func; /* function index in the stack */
|
||||
StkId top; /* top for this function */
|
||||
struct CallInfo {
|
||||
StkIdRel func; /* function index in the stack */
|
||||
StkIdRel top; /* top for this function */
|
||||
struct CallInfo *previous, *next; /* dynamic call link */
|
||||
union {
|
||||
struct { /* only for Lua functions */
|
||||
const Instruction *savedpc;
|
||||
volatile l_signalT trap;
|
||||
volatile l_signalT trap; /* function is tracing lines/counts */
|
||||
int nextraargs; /* # of extra arguments in vararg functions */
|
||||
} l;
|
||||
struct { /* only for C functions */
|
||||
@@ -189,36 +204,59 @@ typedef struct CallInfo {
|
||||
int funcidx; /* called-function index */
|
||||
int nyield; /* number of values yielded */
|
||||
int nres; /* number of values returned */
|
||||
struct { /* info about transferred values (for call/return hooks) */
|
||||
unsigned short ftransfer; /* offset of first value transferred */
|
||||
unsigned short ntransfer; /* number of values transferred */
|
||||
} transferinfo;
|
||||
} u2;
|
||||
short nresults; /* expected number of results from this function */
|
||||
unsigned short callstatus;
|
||||
} CallInfo;
|
||||
l_uint32 callstatus;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Maximum expected number of results from a function
|
||||
** (must fit in CIST_NRESULTS).
|
||||
*/
|
||||
#define MAXRESULTS 250
|
||||
|
||||
|
||||
/*
|
||||
** Bits in CallInfo status
|
||||
*/
|
||||
#define CIST_OAH (1<<0) /* original value of 'allowhook' */
|
||||
#define CIST_C (1<<1) /* call is running a C function */
|
||||
#define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */
|
||||
#define CIST_HOOKED (1<<3) /* call is running a debug hook */
|
||||
#define CIST_YPCALL (1<<4) /* doing a yieldable protected call */
|
||||
#define CIST_TAIL (1<<5) /* call was tail called */
|
||||
#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */
|
||||
#define CIST_FIN (1<<7) /* call is running a finalizer */
|
||||
#define CIST_TRAN (1<<8) /* 'ci' has transfer information */
|
||||
#define CIST_CLSRET (1<<9) /* function is closing tbc variables */
|
||||
/* Bits 10-12 are used for CIST_RECST (see below) */
|
||||
#define CIST_RECST 10
|
||||
/* bits 0-7 are the expected number of results from this function + 1 */
|
||||
#define CIST_NRESULTS 0xffu
|
||||
|
||||
/* bits 8-11 count call metamethods (and their extra arguments) */
|
||||
#define CIST_CCMT 8 /* the offset, not the mask */
|
||||
#define MAX_CCMT (0xfu << CIST_CCMT)
|
||||
|
||||
/* Bits 12-14 are used for CIST_RECST (see below) */
|
||||
#define CIST_RECST 12 /* the offset, not the mask */
|
||||
|
||||
/* call is running a C function (still in first 16 bits) */
|
||||
#define CIST_C (1u << (CIST_RECST + 3))
|
||||
/* call is on a fresh "luaV_execute" frame */
|
||||
#define CIST_FRESH cast(l_uint32, CIST_C << 1)
|
||||
/* function is closing tbc variables */
|
||||
#define CIST_CLSRET (CIST_FRESH << 1)
|
||||
/* function has tbc variables to close */
|
||||
#define CIST_TBC (CIST_CLSRET << 1)
|
||||
/* original value of 'allowhook' */
|
||||
#define CIST_OAH (CIST_TBC << 1)
|
||||
/* call is running a debug hook */
|
||||
#define CIST_HOOKED (CIST_OAH << 1)
|
||||
/* doing a yieldable protected call */
|
||||
#define CIST_YPCALL (CIST_HOOKED << 1)
|
||||
/* call was tail called */
|
||||
#define CIST_TAIL (CIST_YPCALL << 1)
|
||||
/* last hook called yielded */
|
||||
#define CIST_HOOKYIELD (CIST_TAIL << 1)
|
||||
/* function "called" a finalizer */
|
||||
#define CIST_FIN (CIST_HOOKYIELD << 1)
|
||||
#if defined(LUA_COMPAT_LT_LE)
|
||||
#define CIST_LEQ (1<<13) /* using __lt for __le */
|
||||
/* using __lt for __le */
|
||||
#define CIST_LEQ (CIST_FIN << 1)
|
||||
#endif
|
||||
|
||||
|
||||
#define get_nresults(cs) (cast_int((cs) & CIST_NRESULTS) - 1)
|
||||
|
||||
/*
|
||||
** Field CIST_RECST stores the "recover status", used to keep the error
|
||||
** status while closing to-be-closed variables in coroutines, so that
|
||||
@@ -228,8 +266,8 @@ typedef struct CallInfo {
|
||||
#define getcistrecst(ci) (((ci)->callstatus >> CIST_RECST) & 7)
|
||||
#define setcistrecst(ci,st) \
|
||||
check_exp(((st) & 7) == (st), /* status must fit in three bits */ \
|
||||
((ci)->callstatus = ((ci)->callstatus & ~(7 << CIST_RECST)) \
|
||||
| ((st) << CIST_RECST)))
|
||||
((ci)->callstatus = ((ci)->callstatus & ~(7u << CIST_RECST)) \
|
||||
| (cast(l_uint32, st) << CIST_RECST)))
|
||||
|
||||
|
||||
/* active function is a Lua function */
|
||||
@@ -238,9 +276,53 @@ typedef struct CallInfo {
|
||||
/* call is running Lua code (not a hook) */
|
||||
#define isLuacode(ci) (!((ci)->callstatus & (CIST_C | CIST_HOOKED)))
|
||||
|
||||
/* assume that CIST_OAH has offset 0 and that 'v' is strictly 0/1 */
|
||||
#define setoah(st,v) ((st) = ((st) & ~CIST_OAH) | (v))
|
||||
#define getoah(st) ((st) & CIST_OAH)
|
||||
|
||||
#define setoah(ci,v) \
|
||||
((ci)->callstatus = ((v) ? (ci)->callstatus | CIST_OAH \
|
||||
: (ci)->callstatus & ~CIST_OAH))
|
||||
#define getoah(ci) (((ci)->callstatus & CIST_OAH) ? 1 : 0)
|
||||
|
||||
|
||||
/*
|
||||
** 'per thread' state
|
||||
*/
|
||||
struct lua_State {
|
||||
CommonHeader;
|
||||
lu_byte allowhook;
|
||||
TStatus status;
|
||||
StkIdRel top; /* first free slot in the stack */
|
||||
struct global_State *l_G;
|
||||
CallInfo *ci; /* call info for current function */
|
||||
StkIdRel stack_last; /* end of stack (last element + 1) */
|
||||
StkIdRel stack; /* stack base */
|
||||
UpVal *openupval; /* list of open upvalues in this stack */
|
||||
StkIdRel tbclist; /* list of to-be-closed variables */
|
||||
GCObject *gclist;
|
||||
struct lua_State *twups; /* list of threads with open upvalues */
|
||||
struct lua_longjmp *errorJmp; /* current error recover point */
|
||||
CallInfo base_ci; /* CallInfo for first level (C host) */
|
||||
volatile lua_Hook hook;
|
||||
ptrdiff_t errfunc; /* current error handling function (stack index) */
|
||||
l_uint32 nCcalls; /* number of nested non-yieldable or C calls */
|
||||
int oldpc; /* last pc traced */
|
||||
int nci; /* number of items in 'ci' list */
|
||||
int basehookcount;
|
||||
int hookcount;
|
||||
volatile l_signalT hookmask;
|
||||
struct { /* info about transferred values (for call/return hooks) */
|
||||
int ftransfer; /* offset of first value transferred */
|
||||
int ntransfer; /* number of values transferred */
|
||||
} transferinfo;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** thread state + extra space
|
||||
*/
|
||||
typedef struct LX {
|
||||
lu_byte extra_[LUA_EXTRASPACE];
|
||||
lua_State l;
|
||||
} LX;
|
||||
|
||||
|
||||
/*
|
||||
@@ -249,25 +331,21 @@ typedef struct CallInfo {
|
||||
typedef struct global_State {
|
||||
lua_Alloc frealloc; /* function to reallocate memory */
|
||||
void *ud; /* auxiliary data to 'frealloc' */
|
||||
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' */
|
||||
l_mem GCtotalbytes; /* number of bytes currently allocated + debt */
|
||||
l_mem GCdebt; /* bytes counted but not yet allocated */
|
||||
l_mem GCmarked; /* number of objects marked in a GC cycle */
|
||||
l_mem GCmajorminor; /* auxiliary counter to control major-minor shifts */
|
||||
stringtable strt; /* hash table for strings */
|
||||
TValue l_registry;
|
||||
TValue nilvalue; /* a nil value */
|
||||
unsigned int seed; /* randomized seed for hashes */
|
||||
lu_byte gcparams[LUA_GCPN];
|
||||
lu_byte currentwhite;
|
||||
lu_byte gcstate; /* state of garbage collector */
|
||||
lu_byte gckind; /* kind of GC running */
|
||||
lu_byte gcstopem; /* stops emergency collections */
|
||||
lu_byte genminormul; /* control for minor generational collections */
|
||||
lu_byte genmajormul; /* control for major generational collections */
|
||||
lu_byte gcrunning; /* true if GC is running */
|
||||
lu_byte gcstp; /* control whether GC is running */
|
||||
lu_byte gcemergency; /* true if this is an emergency collection */
|
||||
lu_byte gcpause; /* size of pause between successive GCs */
|
||||
lu_byte gcstepmul; /* GC "speed" */
|
||||
lu_byte gcstepsize; /* (log2 of) GC granularity */
|
||||
GCObject *allgc; /* list of all collectable objects */
|
||||
GCObject **sweepgc; /* current position of sweep in list */
|
||||
GCObject *finobj; /* list of collectable objects with finalizers */
|
||||
@@ -288,46 +366,18 @@ typedef struct global_State {
|
||||
GCObject *finobjrold; /* list of really old objects with finalizers */
|
||||
struct lua_State *twups; /* list of threads with open upvalues */
|
||||
lua_CFunction panic; /* to be called in unprotected errors */
|
||||
struct lua_State *mainthread;
|
||||
TString *memerrmsg; /* message for memory-allocation errors */
|
||||
TString *tmname[TM_N]; /* array with tag-method names */
|
||||
struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */
|
||||
struct Table *mt[LUA_NUMTYPES]; /* 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' */
|
||||
LX mainth; /* main thread of this state */
|
||||
} global_State;
|
||||
|
||||
|
||||
/*
|
||||
** 'per thread' state
|
||||
*/
|
||||
struct lua_State {
|
||||
CommonHeader;
|
||||
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 */
|
||||
StkId stack_last; /* end of stack (last element + 1) */
|
||||
StkId stack; /* stack base */
|
||||
UpVal *openupval; /* list of open upvalues in this stack */
|
||||
StkId tbclist; /* list of to-be-closed variables */
|
||||
GCObject *gclist;
|
||||
struct lua_State *twups; /* list of threads with open upvalues */
|
||||
struct lua_longjmp *errorJmp; /* current error recover point */
|
||||
CallInfo base_ci; /* CallInfo for first level (C calling Lua) */
|
||||
volatile lua_Hook hook;
|
||||
ptrdiff_t errfunc; /* current error handling function (stack index) */
|
||||
l_uint32 nCcalls; /* number of nested (non-yieldable | C) calls */
|
||||
int oldpc; /* last pc traced */
|
||||
int basehookcount;
|
||||
int hookcount;
|
||||
volatile l_signalT hookmask;
|
||||
};
|
||||
|
||||
|
||||
#define G(L) (L->l_G)
|
||||
#define mainthread(G) (&(G)->mainth.l)
|
||||
|
||||
/*
|
||||
** 'g->nilvalue' being a nil value flags that the state was completely
|
||||
@@ -385,19 +435,20 @@ union GCUnion {
|
||||
#define obj2gco(v) check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc))
|
||||
|
||||
|
||||
/* actual number of total bytes allocated */
|
||||
#define gettotalbytes(g) cast(lu_mem, (g)->totalbytes + (g)->GCdebt)
|
||||
/* actual number of total memory allocated */
|
||||
#define gettotalbytes(g) ((g)->GCtotalbytes - (g)->GCdebt)
|
||||
|
||||
|
||||
LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt);
|
||||
LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
|
||||
LUAI_FUNC lu_mem luaE_threadsize (lua_State *L);
|
||||
LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
|
||||
LUAI_FUNC void luaE_freeCI (lua_State *L);
|
||||
LUAI_FUNC void luaE_shrinkCI (lua_State *L);
|
||||
LUAI_FUNC void luaE_checkcstack (lua_State *L);
|
||||
LUAI_FUNC void luaE_incCstack (lua_State *L);
|
||||
LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont);
|
||||
LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where);
|
||||
LUAI_FUNC int luaE_resetthread (lua_State *L, int status);
|
||||
LUAI_FUNC TStatus luaE_resetthread (lua_State *L, TStatus status);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
124
lstring.c
124
lstring.c
@@ -25,7 +25,17 @@
|
||||
/*
|
||||
** Maximum size for string table.
|
||||
*/
|
||||
#define MAXSTRTB cast_int(luaM_limitN(MAX_INT, TString*))
|
||||
#define MAXSTRTB cast_int(luaM_limitN(INT_MAX, TString*))
|
||||
|
||||
/*
|
||||
** Initial size for the string table (must be power of 2).
|
||||
** The Lua core alone registers ~50 strings (reserved words +
|
||||
** metaevent keys + a few others). Libraries would typically add
|
||||
** a few dozens more.
|
||||
*/
|
||||
#if !defined(MINSTRTABSIZE)
|
||||
#define MINSTRTABSIZE 128
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ -36,11 +46,11 @@ int luaS_eqlngstr (TString *a, TString *b) {
|
||||
lua_assert(a->tt == LUA_VLNGSTR && b->tt == LUA_VLNGSTR);
|
||||
return (a == b) || /* same instance or... */
|
||||
((len == b->u.lnglen) && /* equal length and ... */
|
||||
(memcmp(getstr(a), getstr(b), len) == 0)); /* equal contents */
|
||||
(memcmp(getlngstr(a), getlngstr(b), len) == 0)); /* equal contents */
|
||||
}
|
||||
|
||||
|
||||
unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) {
|
||||
unsigned luaS_hash (const char *str, size_t l, unsigned seed) {
|
||||
unsigned int h = seed ^ cast_uint(l);
|
||||
for (; l > 0; l--)
|
||||
h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1]));
|
||||
@@ -48,11 +58,11 @@ unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) {
|
||||
}
|
||||
|
||||
|
||||
unsigned int luaS_hashlongstr (TString *ts) {
|
||||
unsigned luaS_hashlongstr (TString *ts) {
|
||||
lua_assert(ts->tt == LUA_VLNGSTR);
|
||||
if (ts->extra == 0) { /* no hash? */
|
||||
size_t len = ts->u.lnglen;
|
||||
ts->hash = luaS_hash(getstr(ts), len, ts->hash);
|
||||
ts->hash = luaS_hash(getlngstr(ts), len, ts->hash);
|
||||
ts->extra = 1; /* now it has its hash */
|
||||
}
|
||||
return ts->hash;
|
||||
@@ -136,27 +146,43 @@ void luaS_init (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
size_t luaS_sizelngstr (size_t len, int kind) {
|
||||
switch (kind) {
|
||||
case LSTRREG: /* regular long string */
|
||||
/* don't need 'falloc'/'ud', but need space for content */
|
||||
return offsetof(TString, falloc) + (len + 1) * sizeof(char);
|
||||
case LSTRFIX: /* fixed external long string */
|
||||
/* don't need 'falloc'/'ud' */
|
||||
return offsetof(TString, falloc);
|
||||
default: /* external long string with deallocation */
|
||||
lua_assert(kind == LSTRMEM);
|
||||
return sizeof(TString);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** creates a new string object
|
||||
*/
|
||||
static TString *createstrobj (lua_State *L, size_t l, int tag, unsigned int h) {
|
||||
static TString *createstrobj (lua_State *L, size_t totalsize, lu_byte tag,
|
||||
unsigned h) {
|
||||
TString *ts;
|
||||
GCObject *o;
|
||||
size_t totalsize; /* total size of TString object */
|
||||
totalsize = sizelstring(l);
|
||||
o = luaC_newobj(L, tag, totalsize);
|
||||
ts = gco2ts(o);
|
||||
ts->hash = h;
|
||||
ts->extra = 0;
|
||||
getstr(ts)[l] = '\0'; /* ending 0 */
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
TString *luaS_createlngstrobj (lua_State *L, size_t l) {
|
||||
TString *ts = createstrobj(L, l, LUA_VLNGSTR, G(L)->seed);
|
||||
size_t totalsize = luaS_sizelngstr(l, LSTRREG);
|
||||
TString *ts = createstrobj(L, totalsize, LUA_VLNGSTR, G(L)->seed);
|
||||
ts->u.lnglen = l;
|
||||
ts->shrlen = LSTRREG; /* signals that it is a regular long string */
|
||||
ts->contents = cast_charp(ts) + offsetof(TString, falloc);
|
||||
ts->contents[l] = '\0'; /* ending 0 */
|
||||
return ts;
|
||||
}
|
||||
|
||||
@@ -172,9 +198,9 @@ void luaS_remove (lua_State *L, TString *ts) {
|
||||
|
||||
|
||||
static void growstrtab (lua_State *L, stringtable *tb) {
|
||||
if (l_unlikely(tb->nuse == MAX_INT)) { /* too many strings? */
|
||||
if (l_unlikely(tb->nuse == INT_MAX)) { /* too many strings? */
|
||||
luaC_fullgc(L, 1); /* try to free some... */
|
||||
if (tb->nuse == MAX_INT) /* still too many? */
|
||||
if (tb->nuse == INT_MAX) /* still too many? */
|
||||
luaM_error(L); /* cannot even create a message... */
|
||||
}
|
||||
if (tb->size <= MAXSTRTB / 2) /* can grow string table? */
|
||||
@@ -193,7 +219,8 @@ static TString *internshrstr (lua_State *L, const char *str, size_t l) {
|
||||
TString **list = &tb->hash[lmod(h, tb->size)];
|
||||
lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */
|
||||
for (ts = *list; ts != NULL; ts = ts->u.hnext) {
|
||||
if (l == ts->shrlen && (memcmp(str, getstr(ts), l * sizeof(char)) == 0)) {
|
||||
if (l == cast_uint(ts->shrlen) &&
|
||||
(memcmp(str, getshrstr(ts), l * sizeof(char)) == 0)) {
|
||||
/* found! */
|
||||
if (isdead(g, ts)) /* dead (but not collected yet)? */
|
||||
changewhite(ts); /* resurrect it */
|
||||
@@ -205,9 +232,10 @@ static TString *internshrstr (lua_State *L, const char *str, size_t l) {
|
||||
growstrtab(L, tb);
|
||||
list = &tb->hash[lmod(h, tb->size)]; /* rehash with new size */
|
||||
}
|
||||
ts = createstrobj(L, l, LUA_VSHRSTR, h);
|
||||
memcpy(getstr(ts), str, l * sizeof(char));
|
||||
ts->shrlen = cast_byte(l);
|
||||
ts = createstrobj(L, sizestrshr(l), LUA_VSHRSTR, h);
|
||||
ts->shrlen = cast(ls_byte, l);
|
||||
getshrstr(ts)[l] = '\0'; /* ending 0 */
|
||||
memcpy(getshrstr(ts), str, l * sizeof(char));
|
||||
ts->u.hnext = *list;
|
||||
*list = ts;
|
||||
tb->nuse++;
|
||||
@@ -223,10 +251,10 @@ TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
|
||||
return internshrstr(L, str, l);
|
||||
else {
|
||||
TString *ts;
|
||||
if (l_unlikely(l >= (MAX_SIZE - sizeof(TString))/sizeof(char)))
|
||||
if (l_unlikely(l * sizeof(char) >= (MAX_SIZE - sizeof(TString))))
|
||||
luaM_toobig(L);
|
||||
ts = luaS_createlngstrobj(L, l);
|
||||
memcpy(getstr(ts), str, l * sizeof(char));
|
||||
memcpy(getlngstr(ts), str, l * sizeof(char));
|
||||
return ts;
|
||||
}
|
||||
}
|
||||
@@ -255,7 +283,7 @@ TString *luaS_new (lua_State *L, const char *str) {
|
||||
}
|
||||
|
||||
|
||||
Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue) {
|
||||
Udata *luaS_newudata (lua_State *L, size_t s, unsigned short nuvalue) {
|
||||
Udata *u;
|
||||
int i;
|
||||
GCObject *o;
|
||||
@@ -271,3 +299,61 @@ Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue) {
|
||||
return u;
|
||||
}
|
||||
|
||||
|
||||
struct NewExt {
|
||||
ls_byte kind;
|
||||
const char *s;
|
||||
size_t len;
|
||||
TString *ts; /* output */
|
||||
};
|
||||
|
||||
|
||||
static void f_newext (lua_State *L, void *ud) {
|
||||
struct NewExt *ne = cast(struct NewExt *, ud);
|
||||
size_t size = luaS_sizelngstr(0, ne->kind);
|
||||
ne->ts = createstrobj(L, size, LUA_VLNGSTR, G(L)->seed);
|
||||
}
|
||||
|
||||
|
||||
static void f_pintern (lua_State *L, void *ud) {
|
||||
struct NewExt *ne = cast(struct NewExt *, ud);
|
||||
ne->ts = internshrstr(L, ne->s, ne->len);
|
||||
}
|
||||
|
||||
|
||||
TString *luaS_newextlstr (lua_State *L,
|
||||
const char *s, size_t len, lua_Alloc falloc, void *ud) {
|
||||
struct NewExt ne;
|
||||
if (len <= LUAI_MAXSHORTLEN) { /* short string? */
|
||||
ne.s = s; ne.len = len;
|
||||
if (!falloc)
|
||||
f_pintern(L, &ne); /* just internalize string */
|
||||
else {
|
||||
TStatus status = luaD_rawrunprotected(L, f_pintern, &ne);
|
||||
(*falloc)(ud, cast_voidp(s), len + 1, 0); /* free external string */
|
||||
if (status != LUA_OK) /* memory error? */
|
||||
luaM_error(L); /* re-raise memory error */
|
||||
}
|
||||
return ne.ts;
|
||||
}
|
||||
/* "normal" case: long strings */
|
||||
if (!falloc) {
|
||||
ne.kind = LSTRFIX;
|
||||
f_newext(L, &ne); /* just create header */
|
||||
}
|
||||
else {
|
||||
ne.kind = LSTRMEM;
|
||||
if (luaD_rawrunprotected(L, f_newext, &ne) != LUA_OK) { /* mem. error? */
|
||||
(*falloc)(ud, cast_voidp(s), len + 1, 0); /* free external string */
|
||||
luaM_error(L); /* re-raise memory error */
|
||||
}
|
||||
ne.ts->falloc = falloc;
|
||||
ne.ts->ud = ud;
|
||||
}
|
||||
ne.ts->shrlen = ne.kind;
|
||||
ne.ts->u.lnglen = len;
|
||||
ne.ts->contents = cast_charp(s);
|
||||
return ne.ts;
|
||||
}
|
||||
|
||||
|
||||
|
||||
30
lstring.h
30
lstring.h
@@ -20,10 +20,23 @@
|
||||
|
||||
|
||||
/*
|
||||
** Size of a TString: Size of the header plus space for the string
|
||||
** Maximum length for short strings, that is, strings that are
|
||||
** internalized. (Cannot be smaller than reserved words or tags for
|
||||
** metamethods, as these strings must be internalized;
|
||||
** #("function") = 8, #("__newindex") = 10.)
|
||||
*/
|
||||
#if !defined(LUAI_MAXSHORTLEN)
|
||||
#define LUAI_MAXSHORTLEN 40
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Size of a short TString: Size of the header plus space for the string
|
||||
** itself (including final '\0').
|
||||
*/
|
||||
#define sizelstring(l) (offsetof(TString, contents) + ((l) + 1) * sizeof(char))
|
||||
#define sizestrshr(l) \
|
||||
(offsetof(TString, contents) + ((l) + 1) * sizeof(char))
|
||||
|
||||
|
||||
#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \
|
||||
(sizeof(s)/sizeof(char))-1))
|
||||
@@ -32,7 +45,7 @@
|
||||
/*
|
||||
** test whether a string is a reserved word
|
||||
*/
|
||||
#define isreserved(s) ((s)->tt == LUA_VSHRSTR && (s)->extra > 0)
|
||||
#define isreserved(s) (strisshr(s) && (s)->extra > 0)
|
||||
|
||||
|
||||
/*
|
||||
@@ -41,17 +54,20 @@
|
||||
#define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b))
|
||||
|
||||
|
||||
LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed);
|
||||
LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts);
|
||||
LUAI_FUNC unsigned luaS_hash (const char *str, size_t l, unsigned seed);
|
||||
LUAI_FUNC unsigned luaS_hashlongstr (TString *ts);
|
||||
LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b);
|
||||
LUAI_FUNC void luaS_resize (lua_State *L, int newsize);
|
||||
LUAI_FUNC void luaS_clearcache (global_State *g);
|
||||
LUAI_FUNC void luaS_init (lua_State *L);
|
||||
LUAI_FUNC void luaS_remove (lua_State *L, TString *ts);
|
||||
LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue);
|
||||
LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s,
|
||||
unsigned short nuvalue);
|
||||
LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l);
|
||||
LUAI_FUNC TString *luaS_new (lua_State *L, const char *str);
|
||||
LUAI_FUNC TString *luaS_createlngstrobj (lua_State *L, size_t l);
|
||||
|
||||
LUAI_FUNC TString *luaS_newextlstr (lua_State *L,
|
||||
const char *s, size_t len, lua_Alloc falloc, void *ud);
|
||||
LUAI_FUNC size_t luaS_sizelngstr (size_t len, int kind);
|
||||
|
||||
#endif
|
||||
|
||||
378
lstrlib.c
378
lstrlib.c
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
#include "llimits.h"
|
||||
|
||||
|
||||
/*
|
||||
@@ -36,22 +37,6 @@
|
||||
#endif
|
||||
|
||||
|
||||
/* macro to 'unsign' a character */
|
||||
#define uchar(c) ((unsigned char)(c))
|
||||
|
||||
|
||||
/*
|
||||
** Some sizes are better limited to fit in 'int', but must also fit in
|
||||
** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.)
|
||||
*/
|
||||
#define MAX_SIZET ((size_t)(~(size_t)0))
|
||||
|
||||
#define MAXSIZE \
|
||||
(sizeof(size_t) < sizeof(int) ? MAX_SIZET : (size_t)(INT_MAX))
|
||||
|
||||
|
||||
|
||||
|
||||
static int str_len (lua_State *L) {
|
||||
size_t l;
|
||||
luaL_checklstring(L, 1, &l);
|
||||
@@ -128,7 +113,7 @@ static int str_lower (lua_State *L) {
|
||||
const char *s = luaL_checklstring(L, 1, &l);
|
||||
char *p = luaL_buffinitsize(L, &b, l);
|
||||
for (i=0; i<l; i++)
|
||||
p[i] = tolower(uchar(s[i]));
|
||||
p[i] = cast_char(tolower(cast_uchar(s[i])));
|
||||
luaL_pushresultsize(&b, l);
|
||||
return 1;
|
||||
}
|
||||
@@ -141,7 +126,7 @@ static int str_upper (lua_State *L) {
|
||||
const char *s = luaL_checklstring(L, 1, &l);
|
||||
char *p = luaL_buffinitsize(L, &b, l);
|
||||
for (i=0; i<l; i++)
|
||||
p[i] = toupper(uchar(s[i]));
|
||||
p[i] = cast_char(toupper(cast_uchar(s[i])));
|
||||
luaL_pushresultsize(&b, l);
|
||||
return 1;
|
||||
}
|
||||
@@ -154,10 +139,10 @@ static int str_rep (lua_State *L) {
|
||||
const char *sep = luaL_optlstring(L, 3, "", &lsep);
|
||||
if (n <= 0)
|
||||
lua_pushliteral(L, "");
|
||||
else if (l_unlikely(l + lsep < l || l + lsep > MAXSIZE / n))
|
||||
else if (l_unlikely(l + lsep < l || l + lsep > MAX_SIZE / cast_sizet(n)))
|
||||
return luaL_error(L, "resulting string too large");
|
||||
else {
|
||||
size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep;
|
||||
size_t totallen = ((size_t)n * (l + lsep)) - lsep;
|
||||
luaL_Buffer b;
|
||||
char *p = luaL_buffinitsize(L, &b, totallen);
|
||||
while (n-- > 1) { /* first n-1 copies (followed by separator) */
|
||||
@@ -187,7 +172,7 @@ static int str_byte (lua_State *L) {
|
||||
n = (int)(pose - posi) + 1;
|
||||
luaL_checkstack(L, n, "string slice too long");
|
||||
for (i=0; i<n; i++)
|
||||
lua_pushinteger(L, uchar(s[posi+i-1]));
|
||||
lua_pushinteger(L, cast_uchar(s[posi + cast_uint(i) - 1]));
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -196,13 +181,13 @@ static int str_char (lua_State *L) {
|
||||
int n = lua_gettop(L); /* number of arguments */
|
||||
int i;
|
||||
luaL_Buffer b;
|
||||
char *p = luaL_buffinitsize(L, &b, n);
|
||||
char *p = luaL_buffinitsize(L, &b, cast_uint(n));
|
||||
for (i=1; i<=n; i++) {
|
||||
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);
|
||||
p[i - 1] = cast_char(cast_uchar(c));
|
||||
}
|
||||
luaL_pushresultsize(&b, n);
|
||||
luaL_pushresultsize(&b, cast_uint(n));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -225,7 +210,12 @@ static int writer (lua_State *L, const void *b, size_t size, void *ud) {
|
||||
state->init = 1;
|
||||
luaL_buffinit(L, &state->B);
|
||||
}
|
||||
luaL_addlstring(&state->B, (const char *)b, size);
|
||||
if (b == NULL) { /* finishing dump? */
|
||||
luaL_pushresult(&state->B); /* push result */
|
||||
lua_replace(L, 1); /* move it to reserved slot */
|
||||
}
|
||||
else
|
||||
luaL_addlstring(&state->B, (const char *)b, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -233,12 +223,13 @@ static int writer (lua_State *L, const void *b, size_t size, void *ud) {
|
||||
static int str_dump (lua_State *L) {
|
||||
struct str_Writer state;
|
||||
int strip = lua_toboolean(L, 2);
|
||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||
lua_settop(L, 1); /* ensure function is on the top of the stack */
|
||||
luaL_argcheck(L, lua_type(L, 1) == LUA_TFUNCTION && !lua_iscfunction(L, 1),
|
||||
1, "Lua function expected");
|
||||
/* ensure function is on the top of the stack and vacate slot 1 */
|
||||
lua_pushvalue(L, 1);
|
||||
state.init = 0;
|
||||
if (l_unlikely(lua_dump(L, writer, &state, strip) != 0))
|
||||
return luaL_error(L, "unable to dump given function");
|
||||
luaL_pushresult(&state.B);
|
||||
lua_dump(L, writer, &state, strip);
|
||||
lua_settop(L, 1); /* leave final result on top */
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -361,10 +352,10 @@ typedef struct MatchState {
|
||||
const char *p_end; /* end ('\0') of pattern */
|
||||
lua_State *L;
|
||||
int matchdepth; /* control for recursive depth (to avoid C stack overflow) */
|
||||
unsigned char level; /* total number of captures (finished or unfinished) */
|
||||
int level; /* total number of captures (finished or unfinished) */
|
||||
struct {
|
||||
const char *init;
|
||||
ptrdiff_t len;
|
||||
ptrdiff_t len; /* length or special value (CAP_*) */
|
||||
} capture[LUA_MAXCAPTURES];
|
||||
} MatchState;
|
||||
|
||||
@@ -453,15 +444,15 @@ static int matchbracketclass (int c, const char *p, const char *ec) {
|
||||
while (++p < ec) {
|
||||
if (*p == L_ESC) {
|
||||
p++;
|
||||
if (match_class(c, uchar(*p)))
|
||||
if (match_class(c, cast_uchar(*p)))
|
||||
return sig;
|
||||
}
|
||||
else if ((*(p+1) == '-') && (p+2 < ec)) {
|
||||
p+=2;
|
||||
if (uchar(*(p-2)) <= c && c <= uchar(*p))
|
||||
if (cast_uchar(*(p-2)) <= c && c <= cast_uchar(*p))
|
||||
return sig;
|
||||
}
|
||||
else if (uchar(*p) == c) return sig;
|
||||
else if (cast_uchar(*p) == c) return sig;
|
||||
}
|
||||
return !sig;
|
||||
}
|
||||
@@ -472,12 +463,12 @@ static int singlematch (MatchState *ms, const char *s, const char *p,
|
||||
if (s >= ms->src_end)
|
||||
return 0;
|
||||
else {
|
||||
int c = uchar(*s);
|
||||
int c = cast_uchar(*s);
|
||||
switch (*p) {
|
||||
case '.': return 1; /* matches any char */
|
||||
case L_ESC: return match_class(c, uchar(*(p+1)));
|
||||
case L_ESC: return match_class(c, cast_uchar(*(p+1)));
|
||||
case '[': return matchbracketclass(c, p, ep-1);
|
||||
default: return (uchar(*p) == c);
|
||||
default: return (cast_uchar(*p) == c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -559,7 +550,7 @@ static const char *end_capture (MatchState *ms, const char *s,
|
||||
static const char *match_capture (MatchState *ms, const char *s, int l) {
|
||||
size_t len;
|
||||
l = check_capture(ms, l);
|
||||
len = ms->capture[l].len;
|
||||
len = cast_sizet(ms->capture[l].len);
|
||||
if ((size_t)(ms->src_end-s) >= len &&
|
||||
memcmp(ms->capture[l].init, s, len) == 0)
|
||||
return s+len;
|
||||
@@ -570,7 +561,7 @@ static const char *match_capture (MatchState *ms, const char *s, int l) {
|
||||
static const char *match (MatchState *ms, const char *s, const char *p) {
|
||||
if (l_unlikely(ms->matchdepth-- == 0))
|
||||
luaL_error(ms->L, "pattern too complex");
|
||||
init: /* using goto's to optimize tail recursion */
|
||||
init: /* using goto to optimize tail recursion */
|
||||
if (p != ms->p_end) { /* end of pattern? */
|
||||
switch (*p) {
|
||||
case '(': { /* start capture */
|
||||
@@ -606,8 +597,8 @@ static const char *match (MatchState *ms, const char *s, const char *p) {
|
||||
luaL_error(ms->L, "missing '[' after '%%f' in pattern");
|
||||
ep = classend(ms, p); /* points to what is next */
|
||||
previous = (s == ms->src_init) ? '\0' : *(s - 1);
|
||||
if (!matchbracketclass(uchar(previous), p, ep - 1) &&
|
||||
matchbracketclass(uchar(*s), p, ep - 1)) {
|
||||
if (!matchbracketclass(cast_uchar(previous), p, ep - 1) &&
|
||||
matchbracketclass(cast_uchar(*s), p, ep - 1)) {
|
||||
p = ep; goto init; /* return match(ms, s, ep); */
|
||||
}
|
||||
s = NULL; /* match failed */
|
||||
@@ -616,7 +607,7 @@ static const char *match (MatchState *ms, const char *s, const char *p) {
|
||||
case '0': case '1': case '2': case '3':
|
||||
case '4': case '5': case '6': case '7':
|
||||
case '8': case '9': { /* capture results (%0-%9)? */
|
||||
s = match_capture(ms, s, uchar(*(p + 1)));
|
||||
s = match_capture(ms, s, cast_uchar(*(p + 1)));
|
||||
if (s != NULL) {
|
||||
p += 2; goto init; /* return match(ms, s, p + 2) */
|
||||
}
|
||||
@@ -683,7 +674,7 @@ static const char *lmemfind (const char *s1, size_t l1,
|
||||
if (memcmp(init, s2+1, l2) == 0)
|
||||
return init-1;
|
||||
else { /* correct 'l1' and 's1' to try again */
|
||||
l1 -= init-s1;
|
||||
l1 -= ct_diff2sz(init - s1);
|
||||
s1 = init;
|
||||
}
|
||||
}
|
||||
@@ -699,13 +690,13 @@ static const char *lmemfind (const char *s1, size_t l1,
|
||||
** 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,
|
||||
static ptrdiff_t get_onecapture (MatchState *ms, int i, const char *s,
|
||||
const char *e, const char **cap) {
|
||||
if (i >= ms->level) {
|
||||
if (l_unlikely(i != 0))
|
||||
luaL_error(ms->L, "invalid capture index %%%d", i + 1);
|
||||
*cap = s;
|
||||
return e - s;
|
||||
return (e - s);
|
||||
}
|
||||
else {
|
||||
ptrdiff_t capl = ms->capture[i].len;
|
||||
@@ -713,7 +704,8 @@ static size_t get_onecapture (MatchState *ms, int i, const char *s,
|
||||
if (l_unlikely(capl == CAP_UNFINISHED))
|
||||
luaL_error(ms->L, "unfinished capture");
|
||||
else if (capl == CAP_POSITION)
|
||||
lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1);
|
||||
lua_pushinteger(ms->L,
|
||||
ct_diff2S(ms->capture[i].init - ms->src_init) + 1);
|
||||
return capl;
|
||||
}
|
||||
}
|
||||
@@ -727,7 +719,7 @@ static void push_onecapture (MatchState *ms, int i, const char *s,
|
||||
const char *cap;
|
||||
ptrdiff_t l = get_onecapture(ms, i, s, e, &cap);
|
||||
if (l != CAP_POSITION)
|
||||
lua_pushlstring(ms->L, cap, l);
|
||||
lua_pushlstring(ms->L, cap, cast_sizet(l));
|
||||
/* else position was already pushed */
|
||||
}
|
||||
|
||||
@@ -784,8 +776,8 @@ static int str_find_aux (lua_State *L, int find) {
|
||||
/* do a plain search */
|
||||
const char *s2 = lmemfind(s + init, ls - init, p, lp);
|
||||
if (s2) {
|
||||
lua_pushinteger(L, (s2 - s) + 1);
|
||||
lua_pushinteger(L, (s2 - s) + lp);
|
||||
lua_pushinteger(L, ct_diff2S(s2 - s) + 1);
|
||||
lua_pushinteger(L, cast_st2S(ct_diff2sz(s2 - s) + lp));
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
@@ -802,8 +794,8 @@ static int str_find_aux (lua_State *L, int find) {
|
||||
reprepstate(&ms);
|
||||
if ((res=match(&ms, s1, p)) != NULL) {
|
||||
if (find) {
|
||||
lua_pushinteger(L, (s1 - s) + 1); /* start */
|
||||
lua_pushinteger(L, res - s); /* end */
|
||||
lua_pushinteger(L, ct_diff2S(s1 - s) + 1); /* start */
|
||||
lua_pushinteger(L, ct_diff2S(res - s)); /* end */
|
||||
return push_captures(&ms, NULL, 0) + 2;
|
||||
}
|
||||
else
|
||||
@@ -875,23 +867,23 @@ static void add_s (MatchState *ms, luaL_Buffer *b, const char *s,
|
||||
const char *news = lua_tolstring(L, 3, &l);
|
||||
const char *p;
|
||||
while ((p = (char *)memchr(news, L_ESC, l)) != NULL) {
|
||||
luaL_addlstring(b, news, p - news);
|
||||
luaL_addlstring(b, news, ct_diff2sz(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' */
|
||||
luaL_addlstring(b, s, ct_diff2sz(e - s));
|
||||
else if (isdigit(cast_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);
|
||||
luaL_addlstring(b, cap, cast_sizet(resl));
|
||||
}
|
||||
else
|
||||
luaL_error(L, "invalid use of '%c' in replacement string", L_ESC);
|
||||
l -= p + 1 - news;
|
||||
l -= ct_diff2sz(p + 1 - news);
|
||||
news = p + 1;
|
||||
}
|
||||
luaL_addlstring(b, news, l);
|
||||
@@ -926,7 +918,7 @@ static int add_value (MatchState *ms, luaL_Buffer *b, const char *s,
|
||||
}
|
||||
if (!lua_toboolean(L, -1)) { /* nil or false? */
|
||||
lua_pop(L, 1); /* remove value */
|
||||
luaL_addlstring(b, s, e - s); /* keep original text */
|
||||
luaL_addlstring(b, s, ct_diff2sz(e - s)); /* keep original text */
|
||||
return 0; /* no changes */
|
||||
}
|
||||
else if (l_unlikely(!lua_isstring(L, -1)))
|
||||
@@ -945,7 +937,8 @@ static int str_gsub (lua_State *L) {
|
||||
const char *p = luaL_checklstring(L, 2, &lp); /* pattern */
|
||||
const char *lastmatch = NULL; /* end of last match */
|
||||
int tr = lua_type(L, 3); /* replacement type */
|
||||
lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); /* max replacements */
|
||||
/* max replacements */
|
||||
lua_Integer max_s = luaL_optinteger(L, 4, cast_st2S(srcl) + 1);
|
||||
int anchor = (*p == '^');
|
||||
lua_Integer n = 0; /* replacement count */
|
||||
int changed = 0; /* change flag */
|
||||
@@ -975,7 +968,7 @@ static int str_gsub (lua_State *L) {
|
||||
if (!changed) /* no changes? */
|
||||
lua_pushvalue(L, 1); /* return original string */
|
||||
else { /* something changed */
|
||||
luaL_addlstring(&b, src, ms.src_end-src);
|
||||
luaL_addlstring(&b, src, ct_diff2sz(ms.src_end - src));
|
||||
luaL_pushresult(&b); /* create and return new string */
|
||||
}
|
||||
lua_pushinteger(L, n); /* number of substitutions */
|
||||
@@ -1013,15 +1006,15 @@ static int str_gsub (lua_State *L) {
|
||||
/*
|
||||
** Add integer part of 'x' to buffer and return new 'x'
|
||||
*/
|
||||
static lua_Number adddigit (char *buff, int n, lua_Number x) {
|
||||
static lua_Number adddigit (char *buff, unsigned n, lua_Number x) {
|
||||
lua_Number dd = l_mathop(floor)(x); /* get integer part from 'x' */
|
||||
int d = (int)dd;
|
||||
buff[n] = (d < 10 ? d + '0' : d - 10 + 'a'); /* add to buffer */
|
||||
buff[n] = cast_char(d < 10 ? d + '0' : d - 10 + 'a'); /* add to buffer */
|
||||
return x - dd; /* return what is left */
|
||||
}
|
||||
|
||||
|
||||
static int num2straux (char *buff, int sz, lua_Number x) {
|
||||
static int num2straux (char *buff, unsigned sz, lua_Number x) {
|
||||
/* if 'inf' or 'NaN', format it like '%g' */
|
||||
if (x != x || x == (lua_Number)HUGE_VAL || x == -(lua_Number)HUGE_VAL)
|
||||
return l_sprintf(buff, sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)x);
|
||||
@@ -1032,7 +1025,7 @@ static int num2straux (char *buff, int sz, lua_Number x) {
|
||||
else {
|
||||
int e;
|
||||
lua_Number m = l_mathop(frexp)(x, &e); /* 'x' fraction and exponent */
|
||||
int n = 0; /* character count */
|
||||
unsigned n = 0; /* character count */
|
||||
if (m < 0) { /* is number negative? */
|
||||
buff[n++] = '-'; /* add sign */
|
||||
m = -m; /* make it positive */
|
||||
@@ -1046,20 +1039,20 @@ static int num2straux (char *buff, int sz, lua_Number x) {
|
||||
m = adddigit(buff, n++, m * 16);
|
||||
} while (m > 0);
|
||||
}
|
||||
n += l_sprintf(buff + n, sz - n, "p%+d", e); /* add exponent */
|
||||
n += cast_uint(l_sprintf(buff + n, sz - n, "p%+d", e)); /* add exponent */
|
||||
lua_assert(n < sz);
|
||||
return n;
|
||||
return cast_int(n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int lua_number2strx (lua_State *L, char *buff, int sz,
|
||||
static int lua_number2strx (lua_State *L, char *buff, unsigned sz,
|
||||
const char *fmt, lua_Number x) {
|
||||
int n = num2straux(buff, sz, x);
|
||||
if (fmt[SIZELENMOD] == 'A') {
|
||||
int i;
|
||||
for (i = 0; i < n; i++)
|
||||
buff[i] = toupper(uchar(buff[i]));
|
||||
buff[i] = cast_char(toupper(cast_uchar(buff[i])));
|
||||
}
|
||||
else if (l_unlikely(fmt[SIZELENMOD] != 'a'))
|
||||
return luaL_error(L, "modifiers for format '%%a'/'%%A' not implemented");
|
||||
@@ -1090,13 +1083,31 @@ static int lua_number2strx (lua_State *L, char *buff, int sz,
|
||||
|
||||
|
||||
/* valid flags in a format specification */
|
||||
#if !defined(L_FMTFLAGS)
|
||||
#define L_FMTFLAGS "-+ #0"
|
||||
#if !defined(L_FMTFLAGSF)
|
||||
|
||||
/* valid flags for a, A, e, E, f, F, g, and G conversions */
|
||||
#define L_FMTFLAGSF "-+#0 "
|
||||
|
||||
/* valid flags for o, x, and X conversions */
|
||||
#define L_FMTFLAGSX "-#0"
|
||||
|
||||
/* valid flags for d and i conversions */
|
||||
#define L_FMTFLAGSI "-+0 "
|
||||
|
||||
/* valid flags for u conversions */
|
||||
#define L_FMTFLAGSU "-0"
|
||||
|
||||
/* valid flags for c, p, and s conversions */
|
||||
#define L_FMTFLAGSC "-"
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** maximum size of each format specification (such as "%-099.99d")
|
||||
** Maximum size of each format specification (such as "%-099.99d"):
|
||||
** Initial '%', flags (up to 5), width (2), period, precision (2),
|
||||
** length modifier (8), conversion specifier, and final '\0', plus some
|
||||
** extra.
|
||||
*/
|
||||
#define MAX_FORMAT 32
|
||||
|
||||
@@ -1108,12 +1119,12 @@ static void addquoted (luaL_Buffer *b, const char *s, size_t len) {
|
||||
luaL_addchar(b, '\\');
|
||||
luaL_addchar(b, *s);
|
||||
}
|
||||
else if (iscntrl(uchar(*s))) {
|
||||
else if (iscntrl(cast_uchar(*s))) {
|
||||
char buff[10];
|
||||
if (!isdigit(uchar(*(s+1))))
|
||||
l_sprintf(buff, sizeof(buff), "\\%d", (int)uchar(*s));
|
||||
if (!isdigit(cast_uchar(*(s+1))))
|
||||
l_sprintf(buff, sizeof(buff), "\\%d", (int)cast_uchar(*s));
|
||||
else
|
||||
l_sprintf(buff, sizeof(buff), "\\%03d", (int)uchar(*s));
|
||||
l_sprintf(buff, sizeof(buff), "\\%03d", (int)cast_uchar(*s));
|
||||
luaL_addstring(b, buff);
|
||||
}
|
||||
else
|
||||
@@ -1142,9 +1153,9 @@ static int quotefloat (lua_State *L, char *buff, lua_Number n) {
|
||||
int nb = lua_number2strx(L, buff, MAX_ITEM,
|
||||
"%" LUA_NUMBER_FRMLEN "a", n);
|
||||
/* ensures that 'buff' string uses a dot as the radix character */
|
||||
if (memchr(buff, '.', nb) == NULL) { /* no dot? */
|
||||
if (memchr(buff, '.', cast_uint(nb)) == NULL) { /* no dot? */
|
||||
char point = lua_getlocaledecpoint(); /* try locale point */
|
||||
char *ppoint = (char *)memchr(buff, point, nb);
|
||||
char *ppoint = (char *)memchr(buff, point, cast_uint(nb));
|
||||
if (ppoint) *ppoint = '.'; /* change it to a dot */
|
||||
}
|
||||
return nb;
|
||||
@@ -1174,7 +1185,7 @@ static void addliteral (lua_State *L, luaL_Buffer *b, int arg) {
|
||||
: LUA_INTEGER_FMT; /* else use default format */
|
||||
nb = l_sprintf(buff, MAX_ITEM, format, (LUAI_UACINT)n);
|
||||
}
|
||||
luaL_addsize(b, nb);
|
||||
luaL_addsize(b, cast_uint(nb));
|
||||
break;
|
||||
}
|
||||
case LUA_TNIL: case LUA_TBOOLEAN: {
|
||||
@@ -1189,25 +1200,53 @@ static void addliteral (lua_State *L, luaL_Buffer *b, int arg) {
|
||||
}
|
||||
|
||||
|
||||
static const char *scanformat (lua_State *L, const char *strfrmt, char *form) {
|
||||
const char *p = strfrmt;
|
||||
while (*p != '\0' && strchr(L_FMTFLAGS, *p) != NULL) p++; /* skip flags */
|
||||
if ((size_t)(p - strfrmt) >= sizeof(L_FMTFLAGS)/sizeof(char))
|
||||
luaL_error(L, "invalid format (repeated flags)");
|
||||
if (isdigit(uchar(*p))) p++; /* skip width */
|
||||
if (isdigit(uchar(*p))) p++; /* (2 digits at most) */
|
||||
if (*p == '.') {
|
||||
p++;
|
||||
if (isdigit(uchar(*p))) p++; /* skip precision */
|
||||
if (isdigit(uchar(*p))) p++; /* (2 digits at most) */
|
||||
static const char *get2digits (const char *s) {
|
||||
if (isdigit(cast_uchar(*s))) {
|
||||
s++;
|
||||
if (isdigit(cast_uchar(*s))) s++; /* (2 digits at most) */
|
||||
}
|
||||
if (isdigit(uchar(*p)))
|
||||
luaL_error(L, "invalid format (width or precision too long)");
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check whether a conversion specification is valid. When called,
|
||||
** first character in 'form' must be '%' and last character must
|
||||
** be a valid conversion specifier. 'flags' are the accepted flags;
|
||||
** 'precision' signals whether to accept a precision.
|
||||
*/
|
||||
static void checkformat (lua_State *L, const char *form, const char *flags,
|
||||
int precision) {
|
||||
const char *spec = form + 1; /* skip '%' */
|
||||
spec += strspn(spec, flags); /* skip flags */
|
||||
if (*spec != '0') { /* a width cannot start with '0' */
|
||||
spec = get2digits(spec); /* skip width */
|
||||
if (*spec == '.' && precision) {
|
||||
spec++;
|
||||
spec = get2digits(spec); /* skip precision */
|
||||
}
|
||||
}
|
||||
if (!isalpha(cast_uchar(*spec))) /* did not go to the end? */
|
||||
luaL_error(L, "invalid conversion specification: '%s'", form);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Get a conversion specification and copy it to 'form'.
|
||||
** Return the address of its last character.
|
||||
*/
|
||||
static const char *getformat (lua_State *L, const char *strfrmt,
|
||||
char *form) {
|
||||
/* spans flags, width, and precision ('0' is included as a flag) */
|
||||
size_t len = strspn(strfrmt, L_FMTFLAGSF "123456789.");
|
||||
len++; /* adds following character (should be the specifier) */
|
||||
/* still needs space for '%', '\0', plus a length modifier */
|
||||
if (len >= MAX_FORMAT - 10)
|
||||
luaL_error(L, "invalid format (too long)");
|
||||
*(form++) = '%';
|
||||
memcpy(form, strfrmt, ((p - strfrmt) + 1) * sizeof(char));
|
||||
form += (p - strfrmt) + 1;
|
||||
*form = '\0';
|
||||
return p;
|
||||
memcpy(form, strfrmt, len * sizeof(char));
|
||||
*(form + len) = '\0';
|
||||
return strfrmt + len - 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1230,6 +1269,7 @@ static int str_format (lua_State *L) {
|
||||
size_t sfl;
|
||||
const char *strfrmt = luaL_checklstring(L, arg, &sfl);
|
||||
const char *strfrmt_end = strfrmt+sfl;
|
||||
const char *flags;
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
while (strfrmt < strfrmt_end) {
|
||||
@@ -1239,25 +1279,35 @@ static int str_format (lua_State *L) {
|
||||
luaL_addchar(&b, *strfrmt++); /* %% */
|
||||
else { /* format item */
|
||||
char form[MAX_FORMAT]; /* to store the format ('%...') */
|
||||
int maxitem = MAX_ITEM;
|
||||
char *buff = luaL_prepbuffsize(&b, maxitem); /* to put formatted item */
|
||||
int nb = 0; /* number of bytes in added item */
|
||||
unsigned maxitem = MAX_ITEM; /* maximum length for the result */
|
||||
char *buff = luaL_prepbuffsize(&b, maxitem); /* to put result */
|
||||
int nb = 0; /* number of bytes in result */
|
||||
if (++arg > top)
|
||||
return luaL_argerror(L, arg, "no value");
|
||||
strfrmt = scanformat(L, strfrmt, form);
|
||||
strfrmt = getformat(L, strfrmt, form);
|
||||
switch (*strfrmt++) {
|
||||
case 'c': {
|
||||
checkformat(L, form, L_FMTFLAGSC, 0);
|
||||
nb = l_sprintf(buff, maxitem, form, (int)luaL_checkinteger(L, arg));
|
||||
break;
|
||||
}
|
||||
case 'd': case 'i':
|
||||
case 'o': case 'u': case 'x': case 'X': {
|
||||
flags = L_FMTFLAGSI;
|
||||
goto intcase;
|
||||
case 'u':
|
||||
flags = L_FMTFLAGSU;
|
||||
goto intcase;
|
||||
case 'o': case 'x': case 'X':
|
||||
flags = L_FMTFLAGSX;
|
||||
intcase: {
|
||||
lua_Integer n = luaL_checkinteger(L, arg);
|
||||
checkformat(L, form, flags, 1);
|
||||
addlenmod(form, LUA_INTEGER_FRMLEN);
|
||||
nb = l_sprintf(buff, maxitem, form, (LUAI_UACINT)n);
|
||||
break;
|
||||
}
|
||||
case 'a': case 'A':
|
||||
checkformat(L, form, L_FMTFLAGSF, 1);
|
||||
addlenmod(form, LUA_NUMBER_FRMLEN);
|
||||
nb = lua_number2strx(L, buff, maxitem, form,
|
||||
luaL_checknumber(L, arg));
|
||||
@@ -1268,12 +1318,14 @@ static int str_format (lua_State *L) {
|
||||
/* FALLTHROUGH */
|
||||
case 'e': case 'E': case 'g': case 'G': {
|
||||
lua_Number n = luaL_checknumber(L, arg);
|
||||
checkformat(L, form, L_FMTFLAGSF, 1);
|
||||
addlenmod(form, LUA_NUMBER_FRMLEN);
|
||||
nb = l_sprintf(buff, maxitem, form, (LUAI_UACNUMBER)n);
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
const void *p = lua_topointer(L, arg);
|
||||
checkformat(L, form, L_FMTFLAGSC, 0);
|
||||
if (p == NULL) { /* avoid calling 'printf' with argument NULL */
|
||||
p = "(null)"; /* result */
|
||||
form[strlen(form) - 1] = 's'; /* format it as a string */
|
||||
@@ -1294,7 +1346,8 @@ static int str_format (lua_State *L) {
|
||||
luaL_addvalue(&b); /* keep entire string */
|
||||
else {
|
||||
luaL_argcheck(L, l == strlen(s), arg, "string contains zeros");
|
||||
if (!strchr(form, '.') && l >= 100) {
|
||||
checkformat(L, form, L_FMTFLAGSC, 1);
|
||||
if (strchr(form, '.') == NULL && l >= 100) {
|
||||
/* no precision and string is too long to be formatted */
|
||||
luaL_addvalue(&b); /* keep entire string */
|
||||
}
|
||||
@@ -1309,8 +1362,8 @@ static int str_format (lua_State *L) {
|
||||
return luaL_error(L, "invalid conversion '%s' to 'format'", form);
|
||||
}
|
||||
}
|
||||
lua_assert(nb < maxitem);
|
||||
luaL_addsize(&b, nb);
|
||||
lua_assert(cast_uint(nb) < maxitem);
|
||||
luaL_addsize(&b, cast_uint(nb));
|
||||
}
|
||||
}
|
||||
luaL_pushresult(&b);
|
||||
@@ -1352,22 +1405,13 @@ static const union {
|
||||
} nativeendian = {1};
|
||||
|
||||
|
||||
/* dummy structure to get native alignment requirements */
|
||||
struct cD {
|
||||
char c;
|
||||
union { double d; void *p; lua_Integer i; lua_Number n; } u;
|
||||
};
|
||||
|
||||
#define MAXALIGN (offsetof(struct cD, u))
|
||||
|
||||
|
||||
/*
|
||||
** information to pack/unpack stuff
|
||||
*/
|
||||
typedef struct Header {
|
||||
lua_State *L;
|
||||
int islittle;
|
||||
int maxalign;
|
||||
unsigned maxalign;
|
||||
} Header;
|
||||
|
||||
|
||||
@@ -1395,14 +1439,14 @@ typedef enum KOption {
|
||||
*/
|
||||
static int digit (int c) { return '0' <= c && c <= '9'; }
|
||||
|
||||
static int getnum (const char **fmt, int df) {
|
||||
static size_t getnum (const char **fmt, size_t df) {
|
||||
if (!digit(**fmt)) /* no number? */
|
||||
return df; /* return default value */
|
||||
else {
|
||||
int a = 0;
|
||||
size_t a = 0;
|
||||
do {
|
||||
a = a*10 + (*((*fmt)++) - '0');
|
||||
} while (digit(**fmt) && a <= ((int)MAXSIZE - 9)/10);
|
||||
a = a*10 + cast_uint(*((*fmt)++) - '0');
|
||||
} while (digit(**fmt) && a <= (MAX_SIZE - 9)/10);
|
||||
return a;
|
||||
}
|
||||
}
|
||||
@@ -1410,14 +1454,14 @@ static int getnum (const char **fmt, int df) {
|
||||
|
||||
/*
|
||||
** Read an integer numeral and raises an error if it is larger
|
||||
** than the maximum size for integers.
|
||||
** than the maximum size of integers.
|
||||
*/
|
||||
static int getnumlimit (Header *h, const char **fmt, int df) {
|
||||
int sz = getnum(fmt, df);
|
||||
if (l_unlikely(sz > MAXINTSIZE || sz <= 0))
|
||||
return luaL_error(h->L, "integral size (%d) out of limits [1,%d]",
|
||||
sz, MAXINTSIZE);
|
||||
return sz;
|
||||
static unsigned getnumlimit (Header *h, const char **fmt, size_t df) {
|
||||
size_t sz = getnum(fmt, df);
|
||||
if (l_unlikely((sz - 1u) >= MAXINTSIZE))
|
||||
return cast_uint(luaL_error(h->L,
|
||||
"integral size (%d) out of limits [1,%d]", sz, MAXINTSIZE));
|
||||
return cast_uint(sz);
|
||||
}
|
||||
|
||||
|
||||
@@ -1434,7 +1478,9 @@ static void initheader (lua_State *L, Header *h) {
|
||||
/*
|
||||
** Read and classify next option. 'size' is filled with option's size.
|
||||
*/
|
||||
static KOption getoption (Header *h, const char **fmt, int *size) {
|
||||
static KOption getoption (Header *h, const char **fmt, size_t *size) {
|
||||
/* dummy structure to get native alignment requirements */
|
||||
struct cD { char c; union { LUAI_MAXALIGN; } u; };
|
||||
int opt = *((*fmt)++);
|
||||
*size = 0; /* default */
|
||||
switch (opt) {
|
||||
@@ -1454,8 +1500,8 @@ static KOption getoption (Header *h, const char **fmt, int *size) {
|
||||
case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint;
|
||||
case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring;
|
||||
case 'c':
|
||||
*size = getnum(fmt, -1);
|
||||
if (l_unlikely(*size == -1))
|
||||
*size = getnum(fmt, cast_sizet(-1));
|
||||
if (l_unlikely(*size == cast_sizet(-1)))
|
||||
luaL_error(h->L, "missing size for format option 'c'");
|
||||
return Kchar;
|
||||
case 'z': return Kzstr;
|
||||
@@ -1465,7 +1511,11 @@ static KOption getoption (Header *h, const char **fmt, int *size) {
|
||||
case '<': h->islittle = 1; break;
|
||||
case '>': h->islittle = 0; break;
|
||||
case '=': h->islittle = nativeendian.little; break;
|
||||
case '!': h->maxalign = getnumlimit(h, fmt, MAXALIGN); break;
|
||||
case '!': {
|
||||
const size_t maxalign = offsetof(struct cD, u);
|
||||
h->maxalign = getnumlimit(h, fmt, maxalign);
|
||||
break;
|
||||
}
|
||||
default: luaL_error(h->L, "invalid format option '%c'", opt);
|
||||
}
|
||||
return Knop;
|
||||
@@ -1481,10 +1531,10 @@ static KOption getoption (Header *h, const char **fmt, int *size) {
|
||||
** the maximum alignment ('maxalign'). Kchar option needs no alignment
|
||||
** despite its size.
|
||||
*/
|
||||
static KOption getdetails (Header *h, size_t totalsize,
|
||||
const char **fmt, int *psize, int *ntoalign) {
|
||||
static KOption getdetails (Header *h, size_t totalsize, const char **fmt,
|
||||
size_t *psize, unsigned *ntoalign) {
|
||||
KOption opt = getoption(h, fmt, psize);
|
||||
int align = *psize; /* usually, alignment follows size */
|
||||
size_t align = *psize; /* usually, alignment follows size */
|
||||
if (opt == Kpaddalign) { /* 'X' gets alignment from following option */
|
||||
if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0)
|
||||
luaL_argerror(h->L, 1, "invalid next option for option 'X'");
|
||||
@@ -1494,9 +1544,15 @@ static KOption getdetails (Header *h, size_t totalsize,
|
||||
else {
|
||||
if (align > h->maxalign) /* enforce maximum alignment */
|
||||
align = h->maxalign;
|
||||
if (l_unlikely((align & (align - 1)) != 0)) /* not a power of 2? */
|
||||
if (l_unlikely(!ispow2(align))) { /* not a power of 2? */
|
||||
*ntoalign = 0; /* to avoid warnings */
|
||||
luaL_argerror(h->L, 1, "format asks for alignment not power of 2");
|
||||
*ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1);
|
||||
}
|
||||
else {
|
||||
/* 'szmoda' = totalsize % align */
|
||||
unsigned szmoda = cast_uint(totalsize & (align - 1));
|
||||
*ntoalign = cast_uint((align - szmoda) & (align - 1));
|
||||
}
|
||||
}
|
||||
return opt;
|
||||
}
|
||||
@@ -1509,9 +1565,9 @@ static KOption getdetails (Header *h, size_t totalsize,
|
||||
** bytes if necessary (by default they would be zeros).
|
||||
*/
|
||||
static void packint (luaL_Buffer *b, lua_Unsigned n,
|
||||
int islittle, int size, int neg) {
|
||||
int islittle, unsigned size, int neg) {
|
||||
char *buff = luaL_prepbuffsize(b, size);
|
||||
int i;
|
||||
unsigned i;
|
||||
buff[islittle ? 0 : size - 1] = (char)(n & MC); /* first byte */
|
||||
for (i = 1; i < size; i++) {
|
||||
n >>= NB;
|
||||
@@ -1530,7 +1586,7 @@ static void packint (luaL_Buffer *b, lua_Unsigned n,
|
||||
** given 'islittle' is different from native endianness.
|
||||
*/
|
||||
static void copywithendian (char *dest, const char *src,
|
||||
int size, int islittle) {
|
||||
unsigned size, int islittle) {
|
||||
if (islittle == nativeendian.little)
|
||||
memcpy(dest, src, size);
|
||||
else {
|
||||
@@ -1551,8 +1607,11 @@ static int str_pack (lua_State *L) {
|
||||
lua_pushnil(L); /* mark to separate arguments from string buffer */
|
||||
luaL_buffinit(L, &b);
|
||||
while (*fmt != '\0') {
|
||||
int size, ntoalign;
|
||||
unsigned ntoalign;
|
||||
size_t size;
|
||||
KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign);
|
||||
luaL_argcheck(L, size + ntoalign <= MAX_SIZE - totalsize, arg,
|
||||
"result too long");
|
||||
totalsize += ntoalign + size;
|
||||
while (ntoalign-- > 0)
|
||||
luaL_addchar(&b, LUAL_PACKPADBYTE); /* fill alignment */
|
||||
@@ -1564,7 +1623,7 @@ static int str_pack (lua_State *L) {
|
||||
lua_Integer lim = (lua_Integer)1 << ((size * NB) - 1);
|
||||
luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow");
|
||||
}
|
||||
packint(&b, (lua_Unsigned)n, h.islittle, size, (n < 0));
|
||||
packint(&b, (lua_Unsigned)n, h.islittle, cast_uint(size), (n < 0));
|
||||
break;
|
||||
}
|
||||
case Kuint: { /* unsigned integers */
|
||||
@@ -1572,7 +1631,7 @@ static int str_pack (lua_State *L) {
|
||||
if (size < SZINT) /* need overflow check? */
|
||||
luaL_argcheck(L, (lua_Unsigned)n < ((lua_Unsigned)1 << (size * NB)),
|
||||
arg, "unsigned overflow");
|
||||
packint(&b, (lua_Unsigned)n, h.islittle, size, 0);
|
||||
packint(&b, (lua_Unsigned)n, h.islittle, cast_uint(size), 0);
|
||||
break;
|
||||
}
|
||||
case Kfloat: { /* C float */
|
||||
@@ -1602,20 +1661,24 @@ static int str_pack (lua_State *L) {
|
||||
case Kchar: { /* fixed-size string */
|
||||
size_t len;
|
||||
const char *s = luaL_checklstring(L, arg, &len);
|
||||
luaL_argcheck(L, len <= (size_t)size, arg,
|
||||
"string longer than given size");
|
||||
luaL_argcheck(L, len <= size, arg, "string longer than given size");
|
||||
luaL_addlstring(&b, s, len); /* add string */
|
||||
while (len++ < (size_t)size) /* pad extra space */
|
||||
luaL_addchar(&b, LUAL_PACKPADBYTE);
|
||||
if (len < size) { /* does it need padding? */
|
||||
size_t psize = size - len; /* pad size */
|
||||
char *buff = luaL_prepbuffsize(&b, psize);
|
||||
memset(buff, LUAL_PACKPADBYTE, psize);
|
||||
luaL_addsize(&b, psize);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Kstring: { /* strings with length count */
|
||||
size_t len;
|
||||
const char *s = luaL_checklstring(L, arg, &len);
|
||||
luaL_argcheck(L, size >= (int)sizeof(size_t) ||
|
||||
len < ((size_t)1 << (size * NB)),
|
||||
luaL_argcheck(L, size >= sizeof(lua_Unsigned) ||
|
||||
len < ((lua_Unsigned)1 << (size * NB)),
|
||||
arg, "string length does not fit in given size");
|
||||
packint(&b, (lua_Unsigned)len, h.islittle, size, 0); /* pack length */
|
||||
/* pack length */
|
||||
packint(&b, (lua_Unsigned)len, h.islittle, cast_uint(size), 0);
|
||||
luaL_addlstring(&b, s, len);
|
||||
totalsize += len;
|
||||
break;
|
||||
@@ -1646,16 +1709,17 @@ static int str_packsize (lua_State *L) {
|
||||
size_t totalsize = 0; /* accumulate total size of result */
|
||||
initheader(L, &h);
|
||||
while (*fmt != '\0') {
|
||||
int size, ntoalign;
|
||||
unsigned ntoalign;
|
||||
size_t size;
|
||||
KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign);
|
||||
luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1,
|
||||
"variable-length format");
|
||||
size += ntoalign; /* total space used by option */
|
||||
luaL_argcheck(L, totalsize <= MAXSIZE - size, 1,
|
||||
"format result too large");
|
||||
luaL_argcheck(L, totalsize <= LUA_MAXINTEGER - size,
|
||||
1, "format result too large");
|
||||
totalsize += size;
|
||||
}
|
||||
lua_pushinteger(L, (lua_Integer)totalsize);
|
||||
lua_pushinteger(L, cast_st2S(totalsize));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1704,9 +1768,10 @@ static int str_unpack (lua_State *L) {
|
||||
luaL_argcheck(L, pos <= ld, 3, "initial position out of string");
|
||||
initheader(L, &h);
|
||||
while (*fmt != '\0') {
|
||||
int size, ntoalign;
|
||||
unsigned ntoalign;
|
||||
size_t size;
|
||||
KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign);
|
||||
luaL_argcheck(L, (size_t)ntoalign + size <= ld - pos, 2,
|
||||
luaL_argcheck(L, ntoalign + size <= ld - pos, 2,
|
||||
"data string too short");
|
||||
pos += ntoalign; /* skip alignment */
|
||||
/* stack space for item + next position */
|
||||
@@ -1715,8 +1780,8 @@ static int str_unpack (lua_State *L) {
|
||||
switch (opt) {
|
||||
case Kint:
|
||||
case Kuint: {
|
||||
lua_Integer res = unpackint(L, data + pos, h.islittle, size,
|
||||
(opt == Kint));
|
||||
lua_Integer res = unpackint(L, data + pos, h.islittle,
|
||||
cast_int(size), (opt == Kint));
|
||||
lua_pushinteger(L, res);
|
||||
break;
|
||||
}
|
||||
@@ -1743,7 +1808,8 @@ static int str_unpack (lua_State *L) {
|
||||
break;
|
||||
}
|
||||
case Kstring: {
|
||||
size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0);
|
||||
lua_Unsigned len = (lua_Unsigned)unpackint(L, data + pos,
|
||||
h.islittle, cast_int(size), 0);
|
||||
luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short");
|
||||
lua_pushlstring(L, data + pos + size, len);
|
||||
pos += len; /* skip string */
|
||||
@@ -1763,7 +1829,7 @@ static int str_unpack (lua_State *L) {
|
||||
}
|
||||
pos += size;
|
||||
}
|
||||
lua_pushinteger(L, pos + 1); /* next position */
|
||||
lua_pushinteger(L, cast_st2S(pos) + 1); /* next position */
|
||||
return n + 1;
|
||||
}
|
||||
|
||||
|
||||
148
ltable.h
148
ltable.h
@@ -20,11 +20,21 @@
|
||||
** may have any of these metamethods. (First access that fails after the
|
||||
** clearing will set the bit again.)
|
||||
*/
|
||||
#define invalidateTMcache(t) ((t)->flags &= ~maskflags)
|
||||
#define invalidateTMcache(t) ((t)->flags &= cast_byte(~maskflags))
|
||||
|
||||
|
||||
/* true when 't' is using 'dummynode' as its hash part */
|
||||
#define isdummy(t) ((t)->lastfree == NULL)
|
||||
/*
|
||||
** Bit BITDUMMY set in 'flags' means the table is using the dummy node
|
||||
** for its hash part.
|
||||
*/
|
||||
|
||||
#define BITDUMMY (1 << 6)
|
||||
#define NOTBITDUMMY cast_byte(~BITDUMMY)
|
||||
#define isdummy(t) ((t)->flags & BITDUMMY)
|
||||
|
||||
#define setnodummy(t) ((t)->flags &= NOTBITDUMMY)
|
||||
#define setdummy(t) ((t)->flags |= BITDUMMY)
|
||||
|
||||
|
||||
|
||||
/* allocated size for hash nodes */
|
||||
@@ -35,31 +45,139 @@
|
||||
#define nodefromval(v) cast(Node *, (v))
|
||||
|
||||
|
||||
LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key);
|
||||
|
||||
#define luaH_fastgeti(t,k,res,tag) \
|
||||
{ Table *h = t; lua_Unsigned u = l_castS2U(k) - 1u; \
|
||||
if ((u < h->asize)) { \
|
||||
tag = *getArrTag(h, u); \
|
||||
if (!tagisempty(tag)) { farr2val(h, u, tag, res); }} \
|
||||
else { tag = luaH_getint(h, (k), res); }}
|
||||
|
||||
|
||||
#define luaH_fastseti(t,k,val,hres) \
|
||||
{ Table *h = t; lua_Unsigned u = l_castS2U(k) - 1u; \
|
||||
if ((u < h->asize)) { \
|
||||
lu_byte *tag = getArrTag(h, u); \
|
||||
if (checknoTM(h->metatable, TM_NEWINDEX) || !tagisempty(*tag)) \
|
||||
{ fval2arr(h, u, tag, val); hres = HOK; } \
|
||||
else hres = ~cast_int(u); } \
|
||||
else { hres = luaH_psetint(h, k, val); }}
|
||||
|
||||
|
||||
/* results from pset */
|
||||
#define HOK 0
|
||||
#define HNOTFOUND 1
|
||||
#define HNOTATABLE 2
|
||||
#define HFIRSTNODE 3
|
||||
|
||||
/*
|
||||
** 'luaH_get*' operations set 'res', unless the value is absent, and
|
||||
** return the tag of the result.
|
||||
** The 'luaH_pset*' (pre-set) operations set the given value and return
|
||||
** HOK, unless the original value is absent. In that case, if the key
|
||||
** is really absent, they return HNOTFOUND. Otherwise, if there is a
|
||||
** slot with that key but with no value, 'luaH_pset*' return an encoding
|
||||
** of where the key is (usually called 'hres'). (pset cannot set that
|
||||
** value because there might be a metamethod.) If the slot is in the
|
||||
** hash part, the encoding is (HFIRSTNODE + hash index); if the slot is
|
||||
** in the array part, the encoding is (~array index), a negative value.
|
||||
** The value HNOTATABLE is used by the fast macros to signal that the
|
||||
** value being indexed is not a table.
|
||||
** (The size for the array part is limited by the maximum power of two
|
||||
** that fits in an unsigned integer; that is INT_MAX+1. So, the C-index
|
||||
** ranges from 0, which encodes to -1, to INT_MAX, which encodes to
|
||||
** INT_MIN. The size of the hash part is limited by the maximum power of
|
||||
** two that fits in a signed integer; that is (INT_MAX+1)/2. So, it is
|
||||
** safe to add HFIRSTNODE to any index there.)
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
** The array part of a table is represented by an inverted array of
|
||||
** values followed by an array of tags, to avoid wasting space with
|
||||
** padding. In between them there is an unsigned int, explained later.
|
||||
** The 'array' pointer points between the two arrays, so that values are
|
||||
** indexed with negative indices and tags with non-negative indices.
|
||||
|
||||
Values Tags
|
||||
--------------------------------------------------------
|
||||
... | Value 1 | Value 0 |unsigned|0|1|...
|
||||
--------------------------------------------------------
|
||||
^ t->array
|
||||
|
||||
** All accesses to 't->array' should be through the macros 'getArrTag'
|
||||
** and 'getArrVal'.
|
||||
*/
|
||||
|
||||
/* Computes the address of the tag for the abstract C-index 'k' */
|
||||
#define getArrTag(t,k) (cast(lu_byte*, (t)->array) + sizeof(unsigned) + (k))
|
||||
|
||||
/* Computes the address of the value for the abstract C-index 'k' */
|
||||
#define getArrVal(t,k) ((t)->array - 1 - (k))
|
||||
|
||||
|
||||
/*
|
||||
** The unsigned between the two arrays is used as a hint for #t;
|
||||
** see luaH_getn. It is stored there to avoid wasting space in
|
||||
** the structure Table for tables with no array part.
|
||||
*/
|
||||
#define lenhint(t) cast(unsigned*, (t)->array)
|
||||
|
||||
|
||||
/*
|
||||
** Move TValues to/from arrays, using C indices
|
||||
*/
|
||||
#define arr2obj(h,k,val) \
|
||||
((val)->tt_ = *getArrTag(h,(k)), (val)->value_ = *getArrVal(h,(k)))
|
||||
|
||||
#define obj2arr(h,k,val) \
|
||||
(*getArrTag(h,(k)) = (val)->tt_, *getArrVal(h,(k)) = (val)->value_)
|
||||
|
||||
|
||||
/*
|
||||
** Often, we need to check the tag of a value before moving it. The
|
||||
** following macros also move TValues to/from arrays, but receive the
|
||||
** precomputed tag value or address as an extra argument.
|
||||
*/
|
||||
#define farr2val(h,k,tag,res) \
|
||||
((res)->tt_ = tag, (res)->value_ = *getArrVal(h,(k)))
|
||||
|
||||
#define fval2arr(h,k,tag,val) \
|
||||
(*tag = (val)->tt_, *getArrVal(h,(k)) = (val)->value_)
|
||||
|
||||
|
||||
LUAI_FUNC lu_byte luaH_get (Table *t, const TValue *key, TValue *res);
|
||||
LUAI_FUNC lu_byte luaH_getshortstr (Table *t, TString *key, TValue *res);
|
||||
LUAI_FUNC lu_byte luaH_getstr (Table *t, TString *key, TValue *res);
|
||||
LUAI_FUNC lu_byte luaH_getint (Table *t, lua_Integer key, TValue *res);
|
||||
|
||||
/* Special get for metamethods */
|
||||
LUAI_FUNC const TValue *luaH_Hgetshortstr (Table *t, TString *key);
|
||||
|
||||
LUAI_FUNC int luaH_psetint (Table *t, lua_Integer key, TValue *val);
|
||||
LUAI_FUNC int luaH_psetshortstr (Table *t, TString *key, TValue *val);
|
||||
LUAI_FUNC int luaH_psetstr (Table *t, TString *key, TValue *val);
|
||||
LUAI_FUNC int luaH_pset (Table *t, const TValue *key, TValue *val);
|
||||
|
||||
LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key,
|
||||
TValue *value);
|
||||
LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key);
|
||||
LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key);
|
||||
LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key);
|
||||
LUAI_FUNC void luaH_newkey (lua_State *L, Table *t, const TValue *key,
|
||||
TValue *value);
|
||||
LUAI_FUNC void luaH_set (lua_State *L, Table *t, const TValue *key,
|
||||
TValue *value);
|
||||
|
||||
LUAI_FUNC void luaH_finishset (lua_State *L, Table *t, const TValue *key,
|
||||
const TValue *slot, TValue *value);
|
||||
TValue *value, int hres);
|
||||
LUAI_FUNC Table *luaH_new (lua_State *L);
|
||||
LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned int nasize,
|
||||
unsigned int nhsize);
|
||||
LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize);
|
||||
LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned nasize,
|
||||
unsigned nhsize);
|
||||
LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, unsigned nasize);
|
||||
LUAI_FUNC lu_mem luaH_size (Table *t);
|
||||
LUAI_FUNC void luaH_free (lua_State *L, Table *t);
|
||||
LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key);
|
||||
LUAI_FUNC lua_Unsigned luaH_getn (Table *t);
|
||||
LUAI_FUNC unsigned int luaH_realasize (const Table *t);
|
||||
|
||||
|
||||
#if defined(LUA_DEBUG)
|
||||
LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key);
|
||||
LUAI_FUNC int luaH_isdummy (const Table *t);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
93
ltablib.c
93
ltablib.c
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
#include "llimits.h"
|
||||
|
||||
|
||||
/*
|
||||
@@ -58,9 +59,20 @@ static void checktab (lua_State *L, int arg, int what) {
|
||||
}
|
||||
|
||||
|
||||
static int tcreate (lua_State *L) {
|
||||
lua_Unsigned sizeseq = (lua_Unsigned)luaL_checkinteger(L, 1);
|
||||
lua_Unsigned sizerest = (lua_Unsigned)luaL_optinteger(L, 2, 0);
|
||||
luaL_argcheck(L, sizeseq <= cast_uint(INT_MAX), 1, "out of range");
|
||||
luaL_argcheck(L, sizerest <= cast_uint(INT_MAX), 2, "out of range");
|
||||
lua_createtable(L, cast_int(sizeseq), cast_int(sizerest));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int tinsert (lua_State *L) {
|
||||
lua_Integer e = aux_getn(L, 1, TAB_RW) + 1; /* first empty element */
|
||||
lua_Integer pos; /* where to insert new element */
|
||||
lua_Integer e = aux_getn(L, 1, TAB_RW);
|
||||
e = luaL_intop(+, e, 1); /* first empty element */
|
||||
switch (lua_gettop(L)) {
|
||||
case 2: { /* called with only 2 arguments */
|
||||
pos = e; /* insert new element at the end */
|
||||
@@ -92,7 +104,7 @@ static int tremove (lua_State *L) {
|
||||
lua_Integer pos = luaL_optinteger(L, 2, size);
|
||||
if (pos != size) /* validate 'pos' if given */
|
||||
/* check whether 'pos' is in [1, size + 1] */
|
||||
luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 1,
|
||||
luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 2,
|
||||
"position out of bounds");
|
||||
lua_geti(L, 1, pos); /* result = t[pos] */
|
||||
for ( ; pos < size; pos++) {
|
||||
@@ -147,7 +159,7 @@ static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) {
|
||||
lua_geti(L, 1, i);
|
||||
if (l_unlikely(!lua_isstring(L, -1)))
|
||||
luaL_error(L, "invalid value (%s) at index %I in table for 'concat'",
|
||||
luaL_typename(L, -1), i);
|
||||
luaL_typename(L, -1), (LUAI_UACINT)i);
|
||||
luaL_addvalue(b);
|
||||
}
|
||||
|
||||
@@ -195,7 +207,7 @@ static int tunpack (lua_State *L) {
|
||||
lua_Integer i = luaL_optinteger(L, 2, 1);
|
||||
lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1));
|
||||
if (i > e) return 0; /* empty range */
|
||||
n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */
|
||||
n = l_castS2U(e) - l_castS2U(i); /* number of elements minus 1 */
|
||||
if (l_unlikely(n >= (unsigned int)INT_MAX ||
|
||||
!lua_checkstack(L, (int)(++n))))
|
||||
return luaL_error(L, "too many results to unpack");
|
||||
@@ -219,41 +231,26 @@ static int tunpack (lua_State *L) {
|
||||
*/
|
||||
|
||||
|
||||
/* type for array indices */
|
||||
/*
|
||||
** Type for array indices. These indices are always limited by INT_MAX,
|
||||
** so it is safe to cast them to lua_Integer even for Lua 32 bits.
|
||||
*/
|
||||
typedef unsigned int IdxT;
|
||||
|
||||
|
||||
/* Versions of lua_seti/lua_geti specialized for IdxT */
|
||||
#define geti(L,idt,idx) lua_geti(L, idt, l_castU2S(idx))
|
||||
#define seti(L,idt,idx) lua_seti(L, idt, l_castU2S(idx))
|
||||
|
||||
|
||||
/*
|
||||
** Produce a "random" 'unsigned int' to randomize pivot choice. This
|
||||
** macro is used only when 'sort' detects a big imbalance in the result
|
||||
** of a partition. (If you don't want/need this "randomness", ~0 is a
|
||||
** good choice.)
|
||||
*/
|
||||
#if !defined(l_randomizePivot) /* { */
|
||||
|
||||
#include <time.h>
|
||||
|
||||
/* size of 'e' measured in number of 'unsigned int's */
|
||||
#define sof(e) (sizeof(e) / sizeof(unsigned int))
|
||||
|
||||
/*
|
||||
** Use 'time' and 'clock' as sources of "randomness". Because we don't
|
||||
** know the types 'clock_t' and 'time_t', we cannot cast them to
|
||||
** anything without risking overflows. A safe way to use their values
|
||||
** is to copy them to an array of a known type and use the array values.
|
||||
*/
|
||||
static unsigned int l_randomizePivot (void) {
|
||||
clock_t c = clock();
|
||||
time_t t = time(NULL);
|
||||
unsigned int buff[sof(c) + sof(t)];
|
||||
unsigned int i, rnd = 0;
|
||||
memcpy(buff, &c, sof(c) * sizeof(unsigned int));
|
||||
memcpy(buff + sof(c), &t, sof(t) * sizeof(unsigned int));
|
||||
for (i = 0; i < sof(buff); i++)
|
||||
rnd += buff[i];
|
||||
return rnd;
|
||||
}
|
||||
|
||||
#if !defined(l_randomizePivot)
|
||||
#define l_randomizePivot(L) luaL_makeseed(L)
|
||||
#endif /* } */
|
||||
|
||||
|
||||
@@ -262,8 +259,8 @@ static unsigned int l_randomizePivot (void) {
|
||||
|
||||
|
||||
static void set2 (lua_State *L, IdxT i, IdxT j) {
|
||||
lua_seti(L, 1, i);
|
||||
lua_seti(L, 1, j);
|
||||
seti(L, 1, i);
|
||||
seti(L, 1, j);
|
||||
}
|
||||
|
||||
|
||||
@@ -300,15 +297,15 @@ 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 ((void)lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) {
|
||||
if (l_unlikely(i == up - 1)) /* a[i] < P but a[up - 1] == P ?? */
|
||||
while ((void)geti(L, 1, ++i), sort_comp(L, -1, -2)) {
|
||||
if (l_unlikely(i == up - 1)) /* a[up - 1] < P == a[up - 1] */
|
||||
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 */
|
||||
/* after the loop, a[i] >= P and a[lo .. i - 1] < P (a) */
|
||||
/* next loop: repeat --j while P < a[j] */
|
||||
while ((void)lua_geti(L, 1, --j), sort_comp(L, -3, -1)) {
|
||||
if (l_unlikely(j < i)) /* j < i but a[j] > P ?? */
|
||||
while ((void)geti(L, 1, --j), sort_comp(L, -3, -1)) {
|
||||
if (l_unlikely(j < i)) /* j <= i - 1 and a[j] > P, contradicts (a) */
|
||||
luaL_error(L, "invalid order function for sorting");
|
||||
lua_pop(L, 1); /* remove a[j] */
|
||||
}
|
||||
@@ -332,7 +329,7 @@ static IdxT partition (lua_State *L, IdxT lo, IdxT up) {
|
||||
*/
|
||||
static IdxT choosePivot (IdxT lo, IdxT up, unsigned int rnd) {
|
||||
IdxT r4 = (up - lo) / 4; /* range/4 */
|
||||
IdxT p = rnd % (r4 * 2) + (lo + r4);
|
||||
IdxT p = (rnd ^ lo ^ up) % (r4 * 2) + (lo + r4);
|
||||
lua_assert(lo + r4 <= p && p <= up - r4);
|
||||
return p;
|
||||
}
|
||||
@@ -341,14 +338,13 @@ static IdxT choosePivot (IdxT lo, IdxT up, unsigned int rnd) {
|
||||
/*
|
||||
** Quicksort algorithm (recursive function)
|
||||
*/
|
||||
static void auxsort (lua_State *L, IdxT lo, IdxT up,
|
||||
unsigned int rnd) {
|
||||
static void auxsort (lua_State *L, IdxT lo, IdxT up, unsigned rnd) {
|
||||
while (lo < up) { /* loop for tail recursion */
|
||||
IdxT p; /* Pivot index */
|
||||
IdxT n; /* to be used later */
|
||||
/* sort elements 'lo', 'p', and 'up' */
|
||||
lua_geti(L, 1, lo);
|
||||
lua_geti(L, 1, up);
|
||||
geti(L, 1, lo);
|
||||
geti(L, 1, up);
|
||||
if (sort_comp(L, -1, -2)) /* a[up] < a[lo]? */
|
||||
set2(L, lo, up); /* swap a[lo] - a[up] */
|
||||
else
|
||||
@@ -359,13 +355,13 @@ static void auxsort (lua_State *L, IdxT lo, IdxT up,
|
||||
p = (lo + up)/2; /* middle element is a good pivot */
|
||||
else /* for larger intervals, it is worth a random pivot */
|
||||
p = choosePivot(lo, up, rnd);
|
||||
lua_geti(L, 1, p);
|
||||
lua_geti(L, 1, lo);
|
||||
geti(L, 1, p);
|
||||
geti(L, 1, lo);
|
||||
if (sort_comp(L, -2, -1)) /* a[p] < a[lo]? */
|
||||
set2(L, p, lo); /* swap a[p] - a[lo] */
|
||||
else {
|
||||
lua_pop(L, 1); /* remove a[lo] */
|
||||
lua_geti(L, 1, up);
|
||||
geti(L, 1, up);
|
||||
if (sort_comp(L, -1, -2)) /* a[up] < a[p]? */
|
||||
set2(L, p, up); /* swap a[up] - a[p] */
|
||||
else
|
||||
@@ -373,9 +369,9 @@ static void auxsort (lua_State *L, IdxT lo, IdxT up,
|
||||
}
|
||||
if (up - lo == 2) /* only 3 elements? */
|
||||
return; /* already sorted */
|
||||
lua_geti(L, 1, p); /* get middle element (Pivot) */
|
||||
geti(L, 1, p); /* get middle element (Pivot) */
|
||||
lua_pushvalue(L, -1); /* push Pivot */
|
||||
lua_geti(L, 1, up - 1); /* push a[up - 1] */
|
||||
geti(L, 1, up - 1); /* push a[up - 1] */
|
||||
set2(L, p, up - 1); /* swap Pivot (a[p]) with a[up - 1] */
|
||||
p = partition(L, lo, up);
|
||||
/* a[lo .. p - 1] <= a[p] == P <= a[p + 1 .. up] */
|
||||
@@ -390,7 +386,7 @@ static void auxsort (lua_State *L, IdxT lo, IdxT up,
|
||||
up = p - 1; /* tail call for [lo .. p - 1] (lower interval) */
|
||||
}
|
||||
if ((up - lo) / 128 > n) /* partition too imbalanced? */
|
||||
rnd = l_randomizePivot(); /* try a new randomization */
|
||||
rnd = l_randomizePivot(L); /* try a new randomization */
|
||||
} /* tail call auxsort(L, lo, up, rnd) */
|
||||
}
|
||||
|
||||
@@ -412,6 +408,7 @@ static int sort (lua_State *L) {
|
||||
|
||||
static const luaL_Reg tab_funcs[] = {
|
||||
{"concat", tconcat},
|
||||
{"create", tcreate},
|
||||
{"insert", tinsert},
|
||||
{"pack", tpack},
|
||||
{"unpack", tunpack},
|
||||
|
||||
482
ltests.c
482
ltests.c
@@ -44,7 +44,7 @@
|
||||
void *l_Trick = 0;
|
||||
|
||||
|
||||
#define obj_at(L,k) s2v(L->ci->func + (k))
|
||||
#define obj_at(L,k) s2v(L->ci->func.p + (k))
|
||||
|
||||
|
||||
static int runC (lua_State *L, lua_State *L1, const char *pc);
|
||||
@@ -57,7 +57,7 @@ static void setnameval (lua_State *L, const char *name, int val) {
|
||||
|
||||
|
||||
static void pushobject (lua_State *L, const TValue *o) {
|
||||
setobj2s(L, L->top, o);
|
||||
setobj2s(L, L->top.p, o);
|
||||
api_incr_top(L);
|
||||
}
|
||||
|
||||
@@ -73,8 +73,9 @@ static void badexit (const char *fmt, const char *s1, const char *s2) {
|
||||
|
||||
|
||||
static int tpanic (lua_State *L) {
|
||||
const char *msg = lua_tostring(L, -1);
|
||||
if (msg == NULL) msg = "error object is not a string";
|
||||
const char *msg = (lua_type(L, -1) == LUA_TSTRING)
|
||||
? lua_tostring(L, -1)
|
||||
: "error object is not a string";
|
||||
return (badexit("PANIC: unprotected error in call to Lua API (%s)\n",
|
||||
msg, NULL),
|
||||
0); /* do not return to Lua */
|
||||
@@ -216,7 +217,7 @@ void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) {
|
||||
mc->memlimit = limit ? strtoul(limit, NULL, 10) : ULONG_MAX;
|
||||
}
|
||||
if (block == NULL) {
|
||||
type = (oldsize < LUA_NUMTAGS) ? oldsize : 0;
|
||||
type = (oldsize < LUA_NUMTYPES) ? cast_int(oldsize) : 0;
|
||||
oldsize = 0;
|
||||
}
|
||||
else {
|
||||
@@ -297,13 +298,13 @@ static int testobjref1 (global_State *g, GCObject *f, GCObject *t) {
|
||||
if (isdead(g,t)) return 0;
|
||||
if (issweepphase(g))
|
||||
return 1; /* no invariants */
|
||||
else if (g->gckind == KGC_INC)
|
||||
else if (g->gckind != KGC_GENMINOR)
|
||||
return !(isblack(f) && iswhite(t)); /* basic incremental invariant */
|
||||
else { /* generational mode */
|
||||
if ((getage(f) == G_OLD && isblack(f)) && !isold(t))
|
||||
return 0;
|
||||
if (((getage(f) == G_OLD1 || getage(f) == G_TOUCHED2) && isblack(f)) &&
|
||||
getage(t) == G_NEW)
|
||||
if ((getage(f) == G_OLD1 || getage(f) == G_TOUCHED2) &&
|
||||
getage(t) == G_NEW)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
@@ -324,6 +325,46 @@ void lua_printobj (lua_State *L, struct GCObject *o) {
|
||||
printobj(G(L), o);
|
||||
}
|
||||
|
||||
|
||||
void lua_printvalue (TValue *v) {
|
||||
switch (ttypetag(v)) {
|
||||
case LUA_VNUMINT: case LUA_VNUMFLT: {
|
||||
char buff[LUA_N2SBUFFSZ];
|
||||
unsigned len = luaO_tostringbuff(v, buff);
|
||||
buff[len] = '\0';
|
||||
printf("%s", buff);
|
||||
break;
|
||||
}
|
||||
case LUA_VSHRSTR:
|
||||
printf("'%s'", getstr(tsvalue(v))); break;
|
||||
case LUA_VLNGSTR:
|
||||
printf("'%.30s...'", getstr(tsvalue(v))); break;
|
||||
case LUA_VFALSE:
|
||||
printf("%s", "false"); break;
|
||||
case LUA_VTRUE:
|
||||
printf("%s", "true"); break;
|
||||
case LUA_VLIGHTUSERDATA:
|
||||
printf("light udata: %p", pvalue(v)); break;
|
||||
case LUA_VUSERDATA:
|
||||
printf("full udata: %p", uvalue(v)); break;
|
||||
case LUA_VNIL:
|
||||
printf("nil"); break;
|
||||
case LUA_VLCF:
|
||||
printf("light C function: %p", fvalue(v)); break;
|
||||
case LUA_VCCL:
|
||||
printf("C closure: %p", clCvalue(v)); break;
|
||||
case LUA_VLCL:
|
||||
printf("Lua function: %p", clLvalue(v)); break;
|
||||
case LUA_VTHREAD:
|
||||
printf("thread: %p", thvalue(v)); break;
|
||||
case LUA_VTABLE:
|
||||
printf("table: %p", hvalue(v)); break;
|
||||
default:
|
||||
lua_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int testobjref (global_State *g, GCObject *f, GCObject *t) {
|
||||
int r1 = testobjref1(g, f, t);
|
||||
if (!r1) {
|
||||
@@ -358,16 +399,19 @@ static void checkvalref (global_State *g, GCObject *f, const TValue *t) {
|
||||
|
||||
static void checktable (global_State *g, Table *h) {
|
||||
unsigned int i;
|
||||
unsigned int asize = luaH_realasize(h);
|
||||
unsigned int asize = h->asize;
|
||||
Node *n, *limit = gnode(h, sizenode(h));
|
||||
GCObject *hgc = obj2gco(h);
|
||||
checkobjrefN(g, hgc, h->metatable);
|
||||
for (i = 0; i < asize; i++)
|
||||
checkvalref(g, hgc, &h->array[i]);
|
||||
for (i = 0; i < asize; i++) {
|
||||
TValue aux;
|
||||
arr2obj(h, i, &aux);
|
||||
checkvalref(g, hgc, &aux);
|
||||
}
|
||||
for (n = gnode(h, 0); n < limit; n++) {
|
||||
if (!isempty(gval(n))) {
|
||||
TValue k;
|
||||
getnodekey(g->mainthread, &k, n);
|
||||
getnodekey(mainthread(g), &k, n);
|
||||
assert(!keyisnil(n));
|
||||
checkvalref(g, hgc, &k);
|
||||
checkvalref(g, hgc, gval(n));
|
||||
@@ -419,7 +463,7 @@ static void checkLclosure (global_State *g, LClosure *cl) {
|
||||
if (uv) {
|
||||
checkobjrefN(g, clgc, uv);
|
||||
if (!upisopen(uv))
|
||||
checkvalref(g, obj2gco(uv), uv->v);
|
||||
checkvalref(g, obj2gco(uv), uv->v.p);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -428,7 +472,7 @@ static void checkLclosure (global_State *g, LClosure *cl) {
|
||||
static int lua_checkpc (CallInfo *ci) {
|
||||
if (!isLua(ci)) return 1;
|
||||
else {
|
||||
StkId f = ci->func;
|
||||
StkId f = ci->func.p;
|
||||
Proto *p = clLvalue(s2v(f))->p;
|
||||
return p->code <= ci->u.l.savedpc &&
|
||||
ci->u.l.savedpc <= p->code + p->sizecode;
|
||||
@@ -441,19 +485,19 @@ static void checkstack (global_State *g, lua_State *L1) {
|
||||
CallInfo *ci;
|
||||
UpVal *uv;
|
||||
assert(!isdead(g, L1));
|
||||
if (L1->stack == NULL) { /* incomplete thread? */
|
||||
if (L1->stack.p == NULL) { /* incomplete thread? */
|
||||
assert(L1->openupval == NULL && L1->ci == NULL);
|
||||
return;
|
||||
}
|
||||
for (uv = L1->openupval; uv != NULL; uv = uv->u.open.next)
|
||||
assert(upisopen(uv)); /* must be open */
|
||||
assert(L1->top <= L1->stack_last);
|
||||
assert(L1->tbclist <= L1->top);
|
||||
assert(L1->top.p <= L1->stack_last.p);
|
||||
assert(L1->tbclist.p <= L1->top.p);
|
||||
for (ci = L1->ci; ci != NULL; ci = ci->previous) {
|
||||
assert(ci->top <= L1->stack_last);
|
||||
assert(ci->top.p <= L1->stack_last.p);
|
||||
assert(lua_checkpc(ci));
|
||||
}
|
||||
for (o = L1->stack; o < L1->stack_last; o++)
|
||||
for (o = L1->stack.p; o < L1->stack_last.p; o++)
|
||||
checkliveness(L1, s2v(o)); /* entire stack must have valid values */
|
||||
}
|
||||
|
||||
@@ -465,7 +509,7 @@ static void checkrefs (global_State *g, GCObject *o) {
|
||||
break;
|
||||
}
|
||||
case LUA_VUPVAL: {
|
||||
checkvalref(g, o, gco2upv(o)->v);
|
||||
checkvalref(g, o, gco2upv(o)->v.p);
|
||||
break;
|
||||
}
|
||||
case LUA_VTABLE: {
|
||||
@@ -507,7 +551,8 @@ static void checkrefs (global_State *g, GCObject *o) {
|
||||
** * objects must be old enough for their lists ('listage').
|
||||
** * old objects cannot be white.
|
||||
** * old objects must be black, except for 'touched1', 'old0',
|
||||
** threads, and open upvalues.
|
||||
** threads, and open upvalues.
|
||||
** * 'touched1' objects must be gray.
|
||||
*/
|
||||
static void checkobject (global_State *g, GCObject *o, int maybedead,
|
||||
int listage) {
|
||||
@@ -515,25 +560,26 @@ static void checkobject (global_State *g, GCObject *o, int maybedead,
|
||||
assert(maybedead);
|
||||
else {
|
||||
assert(g->gcstate != GCSpause || iswhite(o));
|
||||
if (g->gckind == KGC_GEN) { /* generational mode? */
|
||||
if (g->gckind == KGC_GENMINOR) { /* generational mode? */
|
||||
assert(getage(o) >= listage);
|
||||
assert(!iswhite(o) || !isold(o));
|
||||
if (isold(o)) {
|
||||
assert(!iswhite(o));
|
||||
assert(isblack(o) ||
|
||||
getage(o) == G_TOUCHED1 ||
|
||||
getage(o) == G_OLD0 ||
|
||||
o->tt == LUA_VTHREAD ||
|
||||
(o->tt == LUA_VUPVAL && upisopen(gco2upv(o))));
|
||||
}
|
||||
assert(getage(o) != G_TOUCHED1 || isgray(o));
|
||||
}
|
||||
checkrefs(g, o);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static lu_mem checkgraylist (global_State *g, GCObject *o) {
|
||||
static l_mem checkgraylist (global_State *g, GCObject *o) {
|
||||
int total = 0; /* count number of elements in the list */
|
||||
((void)g); /* better to keep it available if we need to print an object */
|
||||
cast_void(g); /* better to keep it if we need to print an object */
|
||||
while (o) {
|
||||
assert(!!isgray(o) ^ (getage(o) == G_TOUCHED2));
|
||||
assert(!testbit(o->marked, TESTBIT));
|
||||
@@ -560,8 +606,8 @@ static lu_mem checkgraylist (global_State *g, GCObject *o) {
|
||||
/*
|
||||
** Check objects in gray lists.
|
||||
*/
|
||||
static lu_mem checkgrays (global_State *g) {
|
||||
int total = 0; /* count number of elements in all lists */
|
||||
static l_mem checkgrays (global_State *g) {
|
||||
l_mem total = 0; /* count number of elements in all lists */
|
||||
if (!keepinvariant(g)) return total;
|
||||
total += checkgraylist(g, g->gray);
|
||||
total += checkgraylist(g, g->grayagain);
|
||||
@@ -577,7 +623,7 @@ static lu_mem checkgrays (global_State *g) {
|
||||
** 'count' and check its TESTBIT. (It must have been previously set by
|
||||
** 'checkgraylist'.)
|
||||
*/
|
||||
static void incifingray (global_State *g, GCObject *o, lu_mem *count) {
|
||||
static void incifingray (global_State *g, GCObject *o, l_mem *count) {
|
||||
if (!keepinvariant(g))
|
||||
return; /* gray lists not being kept in these phases */
|
||||
if (o->tt == LUA_VUPVAL) {
|
||||
@@ -594,10 +640,10 @@ static void incifingray (global_State *g, GCObject *o, lu_mem *count) {
|
||||
}
|
||||
|
||||
|
||||
static lu_mem checklist (global_State *g, int maybedead, int tof,
|
||||
static l_mem checklist (global_State *g, int maybedead, int tof,
|
||||
GCObject *newl, GCObject *survival, GCObject *old, GCObject *reallyold) {
|
||||
GCObject *o;
|
||||
lu_mem total = 0; /* number of object that should be in gray lists */
|
||||
l_mem total = 0; /* number of object that should be in gray lists */
|
||||
for (o = newl; o != survival; o = o->next) {
|
||||
checkobject(g, o, maybedead, G_NEW);
|
||||
incifingray(g, o, &total);
|
||||
@@ -626,10 +672,10 @@ int lua_checkmemory (lua_State *L) {
|
||||
global_State *g = G(L);
|
||||
GCObject *o;
|
||||
int maybedead;
|
||||
lu_mem totalin; /* total of objects that are in gray lists */
|
||||
lu_mem totalshould; /* total of objects that should be in gray lists */
|
||||
l_mem totalin; /* total of objects that are in gray lists */
|
||||
l_mem totalshould; /* total of objects that should be in gray lists */
|
||||
if (keepinvariant(g)) {
|
||||
assert(!iswhite(g->mainthread));
|
||||
assert(!iswhite(mainthread(g)));
|
||||
assert(!iswhite(gcvalue(&g->l_registry)));
|
||||
}
|
||||
assert(!isdead(g, gcvalue(&g->l_registry)));
|
||||
@@ -691,6 +737,11 @@ static char *buildop (Proto *p, int pc, char *buff) {
|
||||
GETARG_A(i), GETARG_B(i), GETARG_C(i),
|
||||
GETARG_k(i) ? " (k)" : "");
|
||||
break;
|
||||
case ivABC:
|
||||
sprintf(buff, "%-12s%4d %4d %4d%s", name,
|
||||
GETARG_A(i), GETARG_vB(i), GETARG_vC(i),
|
||||
GETARG_k(i) ? " (k)" : "");
|
||||
break;
|
||||
case iABx:
|
||||
sprintf(buff, "%-12s%4d %4d", name, GETARG_A(i), GETARG_Bx(i));
|
||||
break;
|
||||
@@ -811,25 +862,47 @@ static int listlocals (lua_State *L) {
|
||||
|
||||
|
||||
|
||||
static void printstack (lua_State *L) {
|
||||
void lua_printstack (lua_State *L) {
|
||||
int i;
|
||||
int n = lua_gettop(L);
|
||||
printf("stack: >>\n");
|
||||
for (i = 1; i <= n; i++) {
|
||||
printf("%3d: %s\n", i, luaL_tolstring(L, i, NULL));
|
||||
lua_pop(L, 1);
|
||||
printf("%3d: ", i);
|
||||
lua_printvalue(s2v(L->ci->func.p + i));
|
||||
printf("\n");
|
||||
}
|
||||
printf("<<\n");
|
||||
}
|
||||
|
||||
|
||||
int lua_printallstack (lua_State *L) {
|
||||
StkId p;
|
||||
int i = 1;
|
||||
CallInfo *ci = &L->base_ci;
|
||||
printf("stack: >>\n");
|
||||
for (p = L->stack.p; p < L->top.p; p++) {
|
||||
if (ci != NULL && p == ci->func.p) {
|
||||
printf(" ---\n");
|
||||
if (ci == L->ci)
|
||||
ci = NULL; /* printed last frame */
|
||||
else
|
||||
ci = ci->next;
|
||||
}
|
||||
printf("%3d: ", i++);
|
||||
lua_printvalue(s2v(p));
|
||||
printf("\n");
|
||||
}
|
||||
printf("<<\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int get_limits (lua_State *L) {
|
||||
lua_createtable(L, 0, 6);
|
||||
lua_createtable(L, 0, 5);
|
||||
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, "LFPF", LFIELDS_PER_FLUSH);
|
||||
setnameval(L, "NUM_OPCODES", NUM_OPCODES);
|
||||
return 1;
|
||||
}
|
||||
@@ -837,9 +910,9 @@ static int get_limits (lua_State *L) {
|
||||
|
||||
static int mem_query (lua_State *L) {
|
||||
if (lua_isnone(L, 1)) {
|
||||
lua_pushinteger(L, l_memcontrol.total);
|
||||
lua_pushinteger(L, l_memcontrol.numblocks);
|
||||
lua_pushinteger(L, l_memcontrol.maxmem);
|
||||
lua_pushinteger(L, cast_Integer(l_memcontrol.total));
|
||||
lua_pushinteger(L, cast_Integer(l_memcontrol.numblocks));
|
||||
lua_pushinteger(L, cast_Integer(l_memcontrol.maxmem));
|
||||
return 3;
|
||||
}
|
||||
else if (lua_isnumber(L, 1)) {
|
||||
@@ -851,9 +924,9 @@ static int mem_query (lua_State *L) {
|
||||
else {
|
||||
const char *t = luaL_checkstring(L, 1);
|
||||
int i;
|
||||
for (i = LUA_NUMTAGS - 1; i >= 0; i--) {
|
||||
for (i = LUA_NUMTYPES - 1; i >= 0; i--) {
|
||||
if (strcmp(t, ttypename(i)) == 0) {
|
||||
lua_pushinteger(L, l_memcontrol.objcount[i]);
|
||||
lua_pushinteger(L, cast_Integer(l_memcontrol.objcount[i]));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -864,9 +937,9 @@ static int mem_query (lua_State *L) {
|
||||
|
||||
static int alloc_count (lua_State *L) {
|
||||
if (lua_isnone(L, 1))
|
||||
l_memcontrol.countlimit = ~0L;
|
||||
l_memcontrol.countlimit = cast(unsigned long, ~0L);
|
||||
else
|
||||
l_memcontrol.countlimit = luaL_checkinteger(L, 1);
|
||||
l_memcontrol.countlimit = cast(unsigned long, luaL_checkinteger(L, 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -934,57 +1007,86 @@ static int gc_printobj (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
static const char *const statenames[] = {
|
||||
"propagate", "enteratomic", "atomic", "sweepallgc", "sweepfinobj",
|
||||
"sweeptobefnz", "sweepend", "callfin", "pause", ""};
|
||||
|
||||
static int gc_state (lua_State *L) {
|
||||
static const char *statenames[] = {
|
||||
"propagate", "atomic", "enteratomic", "sweepallgc", "sweepfinobj",
|
||||
"sweeptobefnz", "sweepend", "callfin", "pause", ""};
|
||||
static const int states[] = {
|
||||
GCSpropagate, GCSenteratomic, GCSatomic, GCSswpallgc, GCSswpfinobj,
|
||||
GCSswptobefnz, GCSswpend, GCScallfin, GCSpause, -1};
|
||||
int option = states[luaL_checkoption(L, 1, "", statenames)];
|
||||
global_State *g = G(L);
|
||||
if (option == -1) {
|
||||
lua_pushstring(L, statenames[G(L)->gcstate]);
|
||||
lua_pushstring(L, statenames[g->gcstate]);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
global_State *g = G(L);
|
||||
if (G(L)->gckind == KGC_GEN)
|
||||
if (g->gckind != KGC_INC)
|
||||
luaL_error(L, "cannot change states in generational mode");
|
||||
lua_lock(L);
|
||||
if (option < g->gcstate) { /* must cross 'pause'? */
|
||||
luaC_runtilstate(L, bitmask(GCSpause)); /* run until pause */
|
||||
luaC_runtilstate(L, GCSpause, 1); /* run until pause */
|
||||
}
|
||||
luaC_runtilstate(L, bitmask(option));
|
||||
lua_assert(G(L)->gcstate == option);
|
||||
luaC_runtilstate(L, option, 0); /* do not skip propagation state */
|
||||
lua_assert(g->gcstate == option);
|
||||
lua_unlock(L);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int tracinggc = 0;
|
||||
void luai_tracegctest (lua_State *L, int first) {
|
||||
if (!tracinggc) return;
|
||||
else {
|
||||
global_State *g = G(L);
|
||||
lua_unlock(L);
|
||||
g->gcstp = GCSTPGC;
|
||||
lua_checkstack(L, 10);
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, "tracegc");
|
||||
lua_pushboolean(L, first);
|
||||
lua_call(L, 1, 0);
|
||||
g->gcstp = 0;
|
||||
lua_lock(L);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int tracegc (lua_State *L) {
|
||||
if (lua_isnil(L, 1))
|
||||
tracinggc = 0;
|
||||
else {
|
||||
tracinggc = 1;
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, "tracegc");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hash_query (lua_State *L) {
|
||||
if (lua_isnone(L, 2)) {
|
||||
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "string expected");
|
||||
lua_pushinteger(L, tsvalue(obj_at(L, 1))->hash);
|
||||
lua_pushinteger(L, cast_int(tsvalue(obj_at(L, 1))->hash));
|
||||
}
|
||||
else {
|
||||
TValue *o = obj_at(L, 1);
|
||||
Table *t;
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
t = hvalue(obj_at(L, 2));
|
||||
lua_pushinteger(L, luaH_mainposition(t, o) - t->node);
|
||||
lua_pushinteger(L, cast_Integer(luaH_mainposition(t, o) - t->node));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int stacklevel (lua_State *L) {
|
||||
unsigned long a = 0;
|
||||
lua_pushinteger(L, (L->top - L->stack));
|
||||
int a = 0;
|
||||
lua_pushinteger(L, cast_Integer(L->top.p - L->stack.p));
|
||||
lua_pushinteger(L, stacksize(L));
|
||||
lua_pushinteger(L, L->nCcalls);
|
||||
lua_pushinteger(L, cast_Integer(L->nCcalls));
|
||||
lua_pushinteger(L, L->nci);
|
||||
lua_pushinteger(L, (unsigned long)&a);
|
||||
lua_pushinteger(L, (lua_Integer)(size_t)&a);
|
||||
return 5;
|
||||
}
|
||||
|
||||
@@ -995,20 +1097,23 @@ static int table_query (lua_State *L) {
|
||||
unsigned int asize;
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
t = hvalue(obj_at(L, 1));
|
||||
asize = luaH_realasize(t);
|
||||
asize = t->asize;
|
||||
if (i == -1) {
|
||||
lua_pushinteger(L, asize);
|
||||
lua_pushinteger(L, allocsizenode(t));
|
||||
lua_pushinteger(L, isdummy(t) ? 0 : t->lastfree - t->node);
|
||||
lua_pushinteger(L, t->alimit);
|
||||
return 4;
|
||||
lua_pushinteger(L, cast_Integer(asize));
|
||||
lua_pushinteger(L, cast_Integer(allocsizenode(t)));
|
||||
lua_pushinteger(L, cast_Integer(asize > 0 ? *lenhint(t) : 0));
|
||||
return 3;
|
||||
}
|
||||
else if ((unsigned int)i < asize) {
|
||||
else if (cast_uint(i) < asize) {
|
||||
lua_pushinteger(L, i);
|
||||
pushobject(L, &t->array[i]);
|
||||
if (!tagisempty(*getArrTag(t, i)))
|
||||
arr2obj(t, cast_uint(i), s2v(L->top.p));
|
||||
else
|
||||
setnilvalue(s2v(L->top.p));
|
||||
api_incr_top(L);
|
||||
lua_pushnil(L);
|
||||
}
|
||||
else if ((i -= asize) < sizenode(t)) {
|
||||
else if (cast_uint(i -= cast_int(asize)) < sizenode(t)) {
|
||||
TValue k;
|
||||
getnodekey(L, &k, gnode(t, i));
|
||||
if (!isempty(gval(gnode(t, i))) ||
|
||||
@@ -1018,16 +1123,45 @@ static int table_query (lua_State *L) {
|
||||
}
|
||||
else
|
||||
lua_pushliteral(L, "<undef>");
|
||||
pushobject(L, gval(gnode(t, i)));
|
||||
if (gnext(&t->node[i]) != 0)
|
||||
lua_pushinteger(L, gnext(&t->node[i]));
|
||||
if (!isempty(gval(gnode(t, i))))
|
||||
pushobject(L, gval(gnode(t, i)));
|
||||
else
|
||||
lua_pushnil(L);
|
||||
lua_pushinteger(L, gnext(&t->node[i]));
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
||||
static int gc_query (lua_State *L) {
|
||||
global_State *g = G(L);
|
||||
lua_pushstring(L, g->gckind == KGC_INC ? "inc"
|
||||
: g->gckind == KGC_GENMAJOR ? "genmajor"
|
||||
: "genminor");
|
||||
lua_pushstring(L, statenames[g->gcstate]);
|
||||
lua_pushinteger(L, cast_st2S(gettotalbytes(g)));
|
||||
lua_pushinteger(L, cast_st2S(g->GCdebt));
|
||||
lua_pushinteger(L, cast_st2S(g->GCmarked));
|
||||
lua_pushinteger(L, cast_st2S(g->GCmajorminor));
|
||||
return 6;
|
||||
}
|
||||
|
||||
|
||||
static int test_codeparam (lua_State *L) {
|
||||
lua_Integer p = luaL_checkinteger(L, 1);
|
||||
lua_pushinteger(L, luaO_codeparam(cast_uint(p)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int test_applyparam (lua_State *L) {
|
||||
lua_Integer p = luaL_checkinteger(L, 1);
|
||||
lua_Integer x = luaL_checkinteger(L, 2);
|
||||
lua_pushinteger(L, cast_Integer(luaO_applyparam(cast_byte(p), x)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int string_query (lua_State *L) {
|
||||
stringtable *tb = &G(L)->strt;
|
||||
int s = cast_int(luaL_optinteger(L, 1, 0)) - 1;
|
||||
@@ -1040,7 +1174,7 @@ static int string_query (lua_State *L) {
|
||||
TString *ts;
|
||||
int n = 0;
|
||||
for (ts = tb->hash[s]; ts != NULL; ts = ts->u.hnext) {
|
||||
setsvalue2s(L, L->top, ts);
|
||||
setsvalue2s(L, L->top.p, ts);
|
||||
api_incr_top(L);
|
||||
n++;
|
||||
}
|
||||
@@ -1050,28 +1184,40 @@ static int string_query (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
static int getreftable (lua_State *L) {
|
||||
if (lua_istable(L, 2)) /* is there a table as second argument? */
|
||||
return 2; /* use it as the table */
|
||||
else
|
||||
return LUA_REGISTRYINDEX; /* default is to use the register */
|
||||
}
|
||||
|
||||
|
||||
static int tref (lua_State *L) {
|
||||
int t = getreftable(L);
|
||||
int level = lua_gettop(L);
|
||||
luaL_checkany(L, 1);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushinteger(L, luaL_ref(L, LUA_REGISTRYINDEX));
|
||||
(void)level; /* to avoid warnings */
|
||||
lua_pushinteger(L, luaL_ref(L, t));
|
||||
cast_void(level); /* to avoid warnings */
|
||||
lua_assert(lua_gettop(L) == level+1); /* +1 for result */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int getref (lua_State *L) {
|
||||
int t = getreftable(L);
|
||||
int level = lua_gettop(L);
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_checkinteger(L, 1));
|
||||
(void)level; /* to avoid warnings */
|
||||
lua_rawgeti(L, t, luaL_checkinteger(L, 1));
|
||||
cast_void(level); /* to avoid warnings */
|
||||
lua_assert(lua_gettop(L) == level+1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int unref (lua_State *L) {
|
||||
int t = getreftable(L);
|
||||
int level = lua_gettop(L);
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, cast_int(luaL_checkinteger(L, 1)));
|
||||
(void)level; /* to avoid warnings */
|
||||
luaL_unref(L, t, cast_int(luaL_checkinteger(L, 1)));
|
||||
cast_void(level); /* to avoid warnings */
|
||||
lua_assert(lua_gettop(L) == level);
|
||||
return 0;
|
||||
}
|
||||
@@ -1096,7 +1242,7 @@ static int upvalue (lua_State *L) {
|
||||
|
||||
static int newuserdata (lua_State *L) {
|
||||
size_t size = cast_sizet(luaL_optinteger(L, 1, 0));
|
||||
int nuv = luaL_optinteger(L, 2, 0);
|
||||
int nuv = cast_int(luaL_optinteger(L, 2, 0));
|
||||
char *p = cast_charp(lua_newuserdatauv(L, size, nuv));
|
||||
while (size--) *p++ = '\0';
|
||||
return 1;
|
||||
@@ -1111,7 +1257,7 @@ static int pushuserdata (lua_State *L) {
|
||||
|
||||
|
||||
static int udataval (lua_State *L) {
|
||||
lua_pushinteger(L, cast(long, lua_touserdata(L, 1)));
|
||||
lua_pushinteger(L, cast_st2S(cast_sizet(lua_touserdata(L, 1))));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1147,10 +1293,16 @@ static int num2int (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
static int makeseed (lua_State *L) {
|
||||
lua_pushinteger(L, cast_Integer(luaL_makeseed(L)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int newstate (lua_State *L) {
|
||||
void *ud;
|
||||
lua_Alloc f = lua_getallocf(L, &ud);
|
||||
lua_State *L1 = lua_newstate(f, ud);
|
||||
lua_State *L1 = lua_newstate(f, ud, 0);
|
||||
if (L1) {
|
||||
lua_atpanic(L1, tpanic);
|
||||
lua_pushlightuserdata(L, L1);
|
||||
@@ -1169,31 +1321,16 @@ static lua_State *getstate (lua_State *L) {
|
||||
|
||||
|
||||
static int loadlib (lua_State *L) {
|
||||
static const luaL_Reg libs[] = {
|
||||
{LUA_GNAME, luaopen_base},
|
||||
{"coroutine", luaopen_coroutine},
|
||||
{"debug", luaopen_debug},
|
||||
{"io", luaopen_io},
|
||||
{"os", luaopen_os},
|
||||
{"math", luaopen_math},
|
||||
{"string", luaopen_string},
|
||||
{"table", luaopen_table},
|
||||
{"T", luaB_opentests},
|
||||
{NULL, NULL}
|
||||
};
|
||||
lua_State *L1 = getstate(L);
|
||||
int i;
|
||||
luaL_requiref(L1, "package", luaopen_package, 0);
|
||||
int load = cast_int(luaL_checkinteger(L, 2));
|
||||
int preload = cast_int(luaL_checkinteger(L, 3));
|
||||
luaL_openselectedlibs(L1, load, preload);
|
||||
luaL_requiref(L1, "T", luaB_opentests, 0);
|
||||
lua_assert(lua_type(L1, -1) == LUA_TTABLE);
|
||||
/* 'requiref' should not reload module already loaded... */
|
||||
luaL_requiref(L1, "package", NULL, 1); /* seg. fault if it reloads */
|
||||
luaL_requiref(L1, "T", NULL, 1); /* seg. fault if it reloads */
|
||||
/* ...but should return the same module */
|
||||
lua_assert(lua_compare(L1, -1, -2, LUA_OPEQ));
|
||||
luaL_getsubtable(L1, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
|
||||
for (i = 0; libs[i].name; i++) {
|
||||
lua_pushcfunction(L1, libs[i].func);
|
||||
lua_setfield(L1, -2, libs[i].name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1259,9 +1396,9 @@ static int checkpanic (lua_State *L) {
|
||||
lua_Alloc f = lua_getallocf(L, &ud);
|
||||
b.paniccode = luaL_optstring(L, 2, "");
|
||||
b.L = L;
|
||||
L1 = lua_newstate(f, ud); /* create new state */
|
||||
L1 = lua_newstate(f, ud, 0); /* create new state */
|
||||
if (L1 == NULL) { /* error? */
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, MEMERRMSG);
|
||||
return 1;
|
||||
}
|
||||
lua_atpanic(L1, panicback); /* set its panic function */
|
||||
@@ -1280,6 +1417,37 @@ static int checkpanic (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
static int externKstr (lua_State *L) {
|
||||
size_t len;
|
||||
const char *s = luaL_checklstring(L, 1, &len);
|
||||
lua_pushexternalstring(L, s, len, NULL, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Create a buffer with the content of a given string and then
|
||||
** create an external string using that buffer. Use the allocation
|
||||
** function from Lua to create and free the buffer.
|
||||
*/
|
||||
static int externstr (lua_State *L) {
|
||||
size_t len;
|
||||
const char *s = luaL_checklstring(L, 1, &len);
|
||||
void *ud;
|
||||
lua_Alloc allocf = lua_getallocf(L, &ud); /* get allocation function */
|
||||
/* create the buffer */
|
||||
char *buff = cast_charp((*allocf)(ud, NULL, 0, len + 1));
|
||||
if (buff == NULL) { /* memory error? */
|
||||
lua_pushliteral(L, "not enough memory");
|
||||
lua_error(L); /* raise a memory error */
|
||||
}
|
||||
/* copy string content to buffer, including ending 0 */
|
||||
memcpy(buff, s, (len + 1) * sizeof(char));
|
||||
/* create external string */
|
||||
lua_pushexternalstring(L, buff, len, allocf, ud);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** {====================================================================
|
||||
@@ -1318,6 +1486,16 @@ static int getnum_aux (lua_State *L, lua_State *L1, const char **pc) {
|
||||
(*pc)++;
|
||||
return res;
|
||||
}
|
||||
else if (**pc == '!') {
|
||||
(*pc)++;
|
||||
if (**pc == 'G')
|
||||
res = LUA_RIDX_GLOBALS;
|
||||
else if (**pc == 'M')
|
||||
res = LUA_RIDX_MAINTHREAD;
|
||||
else lua_assert(0);
|
||||
(*pc)++;
|
||||
return res;
|
||||
}
|
||||
else if (**pc == '-') {
|
||||
sig = -1;
|
||||
(*pc)++;
|
||||
@@ -1352,15 +1530,20 @@ static int getindex_aux (lua_State *L, lua_State *L1, const char **pc) {
|
||||
skip(pc);
|
||||
switch (*(*pc)++) {
|
||||
case 'R': return LUA_REGISTRYINDEX;
|
||||
case 'G': return luaL_error(L, "deprecated index 'G'");
|
||||
case 'U': return lua_upvalueindex(getnum_aux(L, L1, pc));
|
||||
default: (*pc)--; return getnum_aux(L, L1, pc);
|
||||
default: {
|
||||
int n;
|
||||
(*pc)--; /* to read again */
|
||||
n = getnum_aux(L, L1, pc);
|
||||
if (n == 0) return 0;
|
||||
else return lua_absindex(L1, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const char *const statcodes[] = {"OK", "YIELD", "ERRRUN",
|
||||
"ERRSYNTAX", MEMERRMSG, "ERRGCMM", "ERRERR"};
|
||||
"ERRSYNTAX", MEMERRMSG, "ERRERR"};
|
||||
|
||||
/*
|
||||
** Avoid these stat codes from being collected, to avoid possible
|
||||
@@ -1403,17 +1586,17 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
|
||||
const char *inst = getstring;
|
||||
if EQ("") return 0;
|
||||
else if EQ("absindex") {
|
||||
lua_pushnumber(L1, lua_absindex(L1, getindex));
|
||||
lua_pushinteger(L1, getindex);
|
||||
}
|
||||
else if EQ("append") {
|
||||
int t = getindex;
|
||||
int i = lua_rawlen(L1, t);
|
||||
int i = cast_int(lua_rawlen(L1, t));
|
||||
lua_rawseti(L1, t, i + 1);
|
||||
}
|
||||
else if EQ("arith") {
|
||||
int op;
|
||||
skip(&pc);
|
||||
op = strchr(ops, *pc++) - ops;
|
||||
op = cast_int(strchr(ops, *pc++) - ops);
|
||||
lua_arith(L1, op);
|
||||
}
|
||||
else if EQ("call") {
|
||||
@@ -1455,11 +1638,12 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
|
||||
}
|
||||
else if EQ("func2num") {
|
||||
lua_CFunction func = lua_tocfunction(L1, getindex);
|
||||
lua_pushnumber(L1, cast_sizet(func));
|
||||
lua_pushinteger(L1, cast_st2S(cast_sizet(func)));
|
||||
}
|
||||
else if EQ("getfield") {
|
||||
int t = getindex;
|
||||
lua_getfield(L1, t, getstring);
|
||||
int tp = lua_getfield(L1, t, getstring);
|
||||
lua_assert(tp == lua_type(L1, -1));
|
||||
}
|
||||
else if EQ("getglobal") {
|
||||
lua_getglobal(L1, getstring);
|
||||
@@ -1469,7 +1653,8 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
|
||||
lua_pushnil(L1);
|
||||
}
|
||||
else if EQ("gettable") {
|
||||
lua_gettable(L1, getindex);
|
||||
int tp = lua_gettable(L1, getindex);
|
||||
lua_assert(tp == lua_type(L1, -1));
|
||||
}
|
||||
else if EQ("gettop") {
|
||||
lua_pushinteger(L1, lua_gettop(L1));
|
||||
@@ -1520,8 +1705,11 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
|
||||
luaL_loadfile(L1, luaL_checkstring(L1, getnum));
|
||||
}
|
||||
else if EQ("loadstring") {
|
||||
const char *s = luaL_checkstring(L1, getnum);
|
||||
luaL_loadstring(L1, s);
|
||||
size_t slen;
|
||||
const char *s = luaL_checklstring(L1, getnum, &slen);
|
||||
const char *name = getstring;
|
||||
const char *mode = getstring;
|
||||
luaL_loadbufferx(L1, s, slen, name, mode);
|
||||
}
|
||||
else if EQ("newmetatable") {
|
||||
lua_pushboolean(L1, luaL_newmetatable(L1, getstring));
|
||||
@@ -1533,16 +1721,16 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
|
||||
lua_newthread(L1);
|
||||
}
|
||||
else if EQ("resetthread") {
|
||||
lua_pushinteger(L1, lua_resetthread(L1));
|
||||
lua_pushinteger(L1, lua_resetthread(L1)); /* deprecated */
|
||||
}
|
||||
else if EQ("newuserdata") {
|
||||
lua_newuserdata(L1, getnum);
|
||||
lua_newuserdata(L1, cast_sizet(getnum));
|
||||
}
|
||||
else if EQ("next") {
|
||||
lua_next(L1, -2);
|
||||
}
|
||||
else if EQ("objsize") {
|
||||
lua_pushinteger(L1, lua_rawlen(L1, getindex));
|
||||
lua_pushinteger(L1, l_castU2S(lua_rawlen(L1, getindex)));
|
||||
}
|
||||
else if EQ("pcall") {
|
||||
int narg = getnum;
|
||||
@@ -1561,10 +1749,10 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
|
||||
else if EQ("printstack") {
|
||||
int n = getnum;
|
||||
if (n != 0) {
|
||||
printf("%s\n", luaL_tolstring(L1, n, NULL));
|
||||
lua_pop(L1, 1);
|
||||
lua_printvalue(s2v(L->ci->func.p + n));
|
||||
printf("\n");
|
||||
}
|
||||
else printstack(L1);
|
||||
else lua_printstack(L1);
|
||||
}
|
||||
else if EQ("print") {
|
||||
const char *msg = getstring;
|
||||
@@ -1649,6 +1837,17 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
|
||||
int nres;
|
||||
status = lua_resume(lua_tothread(L1, i), L, getnum, &nres);
|
||||
}
|
||||
else if EQ("traceback") {
|
||||
const char *msg = getstring;
|
||||
int level = getnum;
|
||||
luaL_traceback(L1, L1, msg, level);
|
||||
}
|
||||
else if EQ("threadstatus") {
|
||||
lua_pushstring(L1, statcodes[lua_status(L1)]);
|
||||
}
|
||||
else if EQ("alloccount") {
|
||||
l_memcontrol.countlimit = cast_uint(getnum);
|
||||
}
|
||||
else if EQ("return") {
|
||||
int n = getnum;
|
||||
if (L1 != L) {
|
||||
@@ -1740,9 +1939,12 @@ static struct X { int x; } x;
|
||||
else if EQ("tostring") {
|
||||
const char *s = lua_tostring(L1, getindex);
|
||||
const char *s1 = lua_pushstring(L1, s);
|
||||
(void)s1; /* to avoid warnings */
|
||||
cast_void(s1); /* to avoid warnings */
|
||||
lua_longassert((s == NULL && s1 == NULL) || strcmp(s, s1) == 0);
|
||||
}
|
||||
else if EQ("Ltolstring") {
|
||||
luaL_tolstring(L1, getindex, NULL);
|
||||
}
|
||||
else if EQ("type") {
|
||||
lua_pushstring(L1, luaL_typename(L1, getnum));
|
||||
}
|
||||
@@ -1772,6 +1974,10 @@ static struct X { int x; } x;
|
||||
else if EQ("closeslot") {
|
||||
lua_closeslot(L1, getnum);
|
||||
}
|
||||
else if EQ("argerror") {
|
||||
int arg = getnum;
|
||||
luaL_argerror(L1, arg, getstring);
|
||||
}
|
||||
else luaL_error(L, "unknown instruction %s", buff);
|
||||
}
|
||||
return 0;
|
||||
@@ -1805,9 +2011,9 @@ static int Cfunc (lua_State *L) {
|
||||
static int Cfunck (lua_State *L, int status, lua_KContext ctx) {
|
||||
lua_pushstring(L, statcodes[status]);
|
||||
lua_setglobal(L, "status");
|
||||
lua_pushinteger(L, ctx);
|
||||
lua_pushinteger(L, cast_Integer(ctx));
|
||||
lua_setglobal(L, "ctx");
|
||||
return runC(L, L, lua_tostring(L, ctx));
|
||||
return runC(L, L, lua_tostring(L, cast_int(ctx)));
|
||||
}
|
||||
|
||||
|
||||
@@ -1900,6 +2106,25 @@ static int coresume (lua_State *L) {
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(LUA_USE_POSIX)
|
||||
|
||||
#define nonblock NULL
|
||||
|
||||
#else
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
static int nonblock (lua_State *L) {
|
||||
FILE *f = cast(luaL_Stream*, luaL_checkudata(L, 1, LUA_FILEHANDLE))->f;
|
||||
int fd = fileno(f);
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
flags |= O_NONBLOCK;
|
||||
fcntl(fd, F_SETFL, flags);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
@@ -1913,6 +2138,7 @@ static const struct luaL_Reg tests_funcs[] = {
|
||||
{"gccolor", gc_color},
|
||||
{"gcage", gc_age},
|
||||
{"gcstate", gc_state},
|
||||
{"tracegc", tracegc},
|
||||
{"pobj", gc_printobj},
|
||||
{"getref", getref},
|
||||
{"hash", hash_query},
|
||||
@@ -1920,6 +2146,7 @@ static const struct luaL_Reg tests_funcs[] = {
|
||||
{"limits", get_limits},
|
||||
{"listcode", listcode},
|
||||
{"printcode", printcode},
|
||||
{"printallstack", lua_printallstack},
|
||||
{"listk", listk},
|
||||
{"listabslineinfo", listabslineinfo},
|
||||
{"listlocals", listlocals},
|
||||
@@ -1928,9 +2155,13 @@ static const struct luaL_Reg tests_funcs[] = {
|
||||
{"newstate", newstate},
|
||||
{"newuserdata", newuserdata},
|
||||
{"num2int", num2int},
|
||||
{"makeseed", makeseed},
|
||||
{"pushuserdata", pushuserdata},
|
||||
{"gcquery", gc_query},
|
||||
{"querystr", string_query},
|
||||
{"querytab", table_query},
|
||||
{"codeparam", test_codeparam},
|
||||
{"applyparam", test_applyparam},
|
||||
{"ref", tref},
|
||||
{"resume", coresume},
|
||||
{"s2d", s2d},
|
||||
@@ -1945,6 +2176,9 @@ static const struct luaL_Reg tests_funcs[] = {
|
||||
{"udataval", udataval},
|
||||
{"unref", unref},
|
||||
{"upvalue", upvalue},
|
||||
{"externKstr", externKstr},
|
||||
{"externstr", externstr},
|
||||
{"nonblock", nonblock},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
45
ltests.h
45
ltests.h
@@ -14,6 +14,7 @@
|
||||
/* test Lua with compatibility code */
|
||||
#define LUA_COMPAT_MATHLIB
|
||||
#define LUA_COMPAT_LT_LE
|
||||
#undef LUA_COMPAT_GLOBAL
|
||||
|
||||
|
||||
#define LUA_DEBUG
|
||||
@@ -44,6 +45,10 @@
|
||||
#define LUA_RAND32
|
||||
|
||||
|
||||
/* test stack reallocation without strict address use */
|
||||
#define LUAI_STRICT_ADDRESS 0
|
||||
|
||||
|
||||
/* memory-allocator control variables */
|
||||
typedef struct Memcontrol {
|
||||
int failnext;
|
||||
@@ -58,13 +63,16 @@ typedef struct Memcontrol {
|
||||
LUA_API Memcontrol l_memcontrol;
|
||||
|
||||
|
||||
#define luai_tracegc(L,f) luai_tracegctest(L, f)
|
||||
LUAI_FUNC void luai_tracegctest (lua_State *L, int first);
|
||||
|
||||
|
||||
/*
|
||||
** generic variable for debug tricks
|
||||
*/
|
||||
extern void *l_Trick;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Function to traverse and check all memory used by Lua
|
||||
*/
|
||||
@@ -77,6 +85,19 @@ struct GCObject;
|
||||
LUAI_FUNC void lua_printobj (lua_State *L, struct GCObject *o);
|
||||
|
||||
|
||||
/*
|
||||
** Function to print a value
|
||||
*/
|
||||
struct TValue;
|
||||
LUAI_FUNC void lua_printvalue (struct TValue *v);
|
||||
|
||||
/*
|
||||
** Function to print the stack
|
||||
*/
|
||||
LUAI_FUNC void lua_printstack (lua_State *L);
|
||||
LUAI_FUNC int lua_printallstack (lua_State *L);
|
||||
|
||||
|
||||
/* test for lock/unlock */
|
||||
|
||||
struct L_EXTRA { int lock; int *plock; };
|
||||
@@ -102,9 +123,10 @@ LUA_API void *debug_realloc (void *ud, void *block,
|
||||
size_t osize, size_t nsize);
|
||||
|
||||
#if defined(lua_c)
|
||||
#define luaL_newstate() lua_newstate(debug_realloc, &l_memcontrol)
|
||||
#define luaL_openlibs(L) \
|
||||
{ (luaL_openlibs)(L); \
|
||||
#define luaL_newstate() \
|
||||
lua_newstate(debug_realloc, &l_memcontrol, luaL_makeseed(NULL))
|
||||
#define luai_openlibs(L) \
|
||||
{ luaL_openlibs(L); \
|
||||
luaL_requiref(L, "T", luaB_opentests, 1); \
|
||||
lua_pop(L, 1); }
|
||||
#endif
|
||||
@@ -121,13 +143,20 @@ LUA_API void *debug_realloc (void *ud, void *block,
|
||||
#define STRCACHE_N 23
|
||||
#define STRCACHE_M 5
|
||||
|
||||
#undef LUAI_USER_ALIGNMENT_T
|
||||
#define LUAI_USER_ALIGNMENT_T union { char b[sizeof(void*) * 8]; }
|
||||
|
||||
/*
|
||||
** This one is not compatible with tests for opcode optimizations,
|
||||
** as it blocks some optimizations
|
||||
#define MAXINDEXRK 0
|
||||
*/
|
||||
|
||||
|
||||
/* make stack-overflow tests run faster */
|
||||
/*
|
||||
** Reduce maximum stack size to make stack-overflow tests run faster.
|
||||
** (But value is still large enough to overflow smaller integers.)
|
||||
*/
|
||||
#undef LUAI_MAXSTACK
|
||||
#define LUAI_MAXSTACK 50000
|
||||
#define LUAI_MAXSTACK 68000
|
||||
|
||||
|
||||
/* test mode uses more stack space */
|
||||
|
||||
79
ltm.c
79
ltm.c
@@ -58,7 +58,7 @@ void luaT_init (lua_State *L) {
|
||||
** tag methods
|
||||
*/
|
||||
const TValue *luaT_gettm (Table *events, TMS event, TString *ename) {
|
||||
const TValue *tm = luaH_getshortstr(events, ename);
|
||||
const TValue *tm = luaH_Hgetshortstr(events, ename);
|
||||
lua_assert(event <= TM_EQ);
|
||||
if (notm(tm)) { /* no tag method? */
|
||||
events->flags |= cast_byte(1u<<event); /* cache this fact */
|
||||
@@ -80,7 +80,7 @@ const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) {
|
||||
default:
|
||||
mt = G(L)->mt[ttype(o)];
|
||||
}
|
||||
return (mt ? luaH_getshortstr(mt, G(L)->tmname[event]) : &G(L)->nilvalue);
|
||||
return (mt ? luaH_Hgetshortstr(mt, G(L)->tmname[event]) : &G(L)->nilvalue);
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ const char *luaT_objtypename (lua_State *L, const TValue *o) {
|
||||
Table *mt;
|
||||
if ((ttistable(o) && (mt = hvalue(o)->metatable) != NULL) ||
|
||||
(ttisfulluserdata(o) && (mt = uvalue(o)->metatable) != NULL)) {
|
||||
const TValue *name = luaH_getshortstr(mt, luaS_new(L, "__name"));
|
||||
const TValue *name = luaH_Hgetshortstr(mt, luaS_new(L, "__name"));
|
||||
if (ttisstring(name)) /* is '__name' a string? */
|
||||
return getstr(tsvalue(name)); /* use it as type name */
|
||||
}
|
||||
@@ -102,12 +102,12 @@ const char *luaT_objtypename (lua_State *L, const TValue *o) {
|
||||
|
||||
void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1,
|
||||
const TValue *p2, const TValue *p3) {
|
||||
StkId func = L->top;
|
||||
StkId func = L->top.p;
|
||||
setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */
|
||||
setobj2s(L, func + 1, p1); /* 1st argument */
|
||||
setobj2s(L, func + 2, p2); /* 2nd argument */
|
||||
setobj2s(L, func + 3, p3); /* 3rd argument */
|
||||
L->top = func + 4;
|
||||
L->top.p = func + 4;
|
||||
/* metamethod may yield only when called from Lua code */
|
||||
if (isLuacode(L->ci))
|
||||
luaD_call(L, func, 0);
|
||||
@@ -116,21 +116,22 @@ void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1,
|
||||
}
|
||||
|
||||
|
||||
void luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1,
|
||||
const TValue *p2, StkId res) {
|
||||
lu_byte luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1,
|
||||
const TValue *p2, StkId res) {
|
||||
ptrdiff_t result = savestack(L, res);
|
||||
StkId func = L->top;
|
||||
StkId func = L->top.p;
|
||||
setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */
|
||||
setobj2s(L, func + 1, p1); /* 1st argument */
|
||||
setobj2s(L, func + 2, p2); /* 2nd argument */
|
||||
L->top += 3;
|
||||
L->top.p += 3;
|
||||
/* metamethod may yield only when called from Lua code */
|
||||
if (isLuacode(L->ci))
|
||||
luaD_call(L, func, 1);
|
||||
else
|
||||
luaD_callnoyield(L, func, 1);
|
||||
res = restorestack(L, result);
|
||||
setobjs2s(L, res, --L->top); /* move result to its place */
|
||||
setobjs2s(L, res, --L->top.p); /* move result to its place */
|
||||
return ttypetag(s2v(res)); /* return tag of the result */
|
||||
}
|
||||
|
||||
|
||||
@@ -139,15 +140,16 @@ static int callbinTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||
const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */
|
||||
if (notm(tm))
|
||||
tm = luaT_gettmbyobj(L, p2, event); /* try second operand */
|
||||
if (notm(tm)) return 0;
|
||||
luaT_callTMres(L, tm, p1, p2, res);
|
||||
return 1;
|
||||
if (notm(tm))
|
||||
return -1; /* tag method not found */
|
||||
else /* call tag method and return the tag of the result */
|
||||
return luaT_callTMres(L, tm, p1, p2, res);
|
||||
}
|
||||
|
||||
|
||||
void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||
StkId res, TMS event) {
|
||||
if (l_unlikely(!callbinTM(L, p1, p2, res, event))) {
|
||||
if (l_unlikely(callbinTM(L, p1, p2, res, event) < 0)) {
|
||||
switch (event) {
|
||||
case TM_BAND: case TM_BOR: case TM_BXOR:
|
||||
case TM_SHL: case TM_SHR: case TM_BNOT: {
|
||||
@@ -164,11 +166,14 @@ void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** The use of 'p1' after 'callbinTM' is safe because, when a tag
|
||||
** method is not found, 'callbinTM' cannot change the stack.
|
||||
*/
|
||||
void luaT_tryconcatTM (lua_State *L) {
|
||||
StkId top = L->top;
|
||||
if (l_unlikely(!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2,
|
||||
TM_CONCAT)))
|
||||
luaG_concaterror(L, s2v(top - 2), s2v(top - 1));
|
||||
StkId p1 = L->top.p - 2; /* first argument */
|
||||
if (l_unlikely(callbinTM(L, s2v(p1), s2v(p1 + 1), p1, TM_CONCAT) < 0))
|
||||
luaG_concaterror(L, s2v(p1), s2v(p1 + 1));
|
||||
}
|
||||
|
||||
|
||||
@@ -200,17 +205,17 @@ void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2,
|
||||
*/
|
||||
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));
|
||||
int tag = callbinTM(L, p1, p2, L->top.p, event); /* try original event */
|
||||
if (tag >= 0) /* found tag method? */
|
||||
return !tagisfalse(tag);
|
||||
#if defined(LUA_COMPAT_LT_LE)
|
||||
else if (event == TM_LE) {
|
||||
/* try '!(p2 < p1)' for '(p1 <= p2)' */
|
||||
L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */
|
||||
if (callbinTM(L, p2, p1, L->top, TM_LT)) {
|
||||
L->ci->callstatus ^= CIST_LEQ; /* clear mark */
|
||||
return l_isfalse(s2v(L->top));
|
||||
}
|
||||
/* else error will remove this 'ci'; no need to clear mark */
|
||||
/* try '!(p2 < p1)' for '(p1 <= p2)' */
|
||||
L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */
|
||||
tag = callbinTM(L, p2, p1, L->top.p, TM_LT);
|
||||
L->ci->callstatus ^= CIST_LEQ; /* clear mark */
|
||||
if (tag >= 0) /* found tag method? */
|
||||
return tagisfalse(tag);
|
||||
}
|
||||
#endif
|
||||
luaG_ordererror(L, p1, p2); /* no metamethod found */
|
||||
@@ -238,20 +243,20 @@ int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
|
||||
void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci,
|
||||
const Proto *p) {
|
||||
int i;
|
||||
int actual = cast_int(L->top - ci->func) - 1; /* number of arguments */
|
||||
int actual = cast_int(L->top.p - ci->func.p) - 1; /* number of arguments */
|
||||
int nextra = actual - nfixparams; /* number of extra arguments */
|
||||
ci->u.l.nextraargs = nextra;
|
||||
luaD_checkstack(L, p->maxstacksize + 1);
|
||||
/* copy function to the top of the stack */
|
||||
setobjs2s(L, L->top++, ci->func);
|
||||
setobjs2s(L, L->top.p++, ci->func.p);
|
||||
/* move fixed parameters to the top of the stack */
|
||||
for (i = 1; i <= nfixparams; i++) {
|
||||
setobjs2s(L, L->top++, ci->func + i);
|
||||
setnilvalue(s2v(ci->func + i)); /* erase original parameter (for GC) */
|
||||
setobjs2s(L, L->top.p++, ci->func.p + i);
|
||||
setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */
|
||||
}
|
||||
ci->func += actual + 1;
|
||||
ci->top += actual + 1;
|
||||
lua_assert(L->top <= ci->top && ci->top <= L->stack_last);
|
||||
ci->func.p += actual + 1;
|
||||
ci->top.p += actual + 1;
|
||||
lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p);
|
||||
}
|
||||
|
||||
|
||||
@@ -260,11 +265,11 @@ void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) {
|
||||
int nextra = ci->u.l.nextraargs;
|
||||
if (wanted < 0) {
|
||||
wanted = nextra; /* get all extra arguments available */
|
||||
checkstackGCp(L, nextra, where); /* ensure stack space */
|
||||
L->top = where + nextra; /* next instruction will need top */
|
||||
checkstackp(L, nextra, where); /* ensure stack space */
|
||||
L->top.p = where + nextra; /* next instruction will need top */
|
||||
}
|
||||
for (i = 0; i < wanted && i < nextra; i++)
|
||||
setobjs2s(L, where + i, ci->func - nextra + i);
|
||||
setobjs2s(L, where + i, ci->func.p - nextra + i);
|
||||
for (; i < wanted; i++) /* complete required results with nil */
|
||||
setnilvalue(s2v(where + i));
|
||||
}
|
||||
|
||||
17
ltm.h
17
ltm.h
@@ -48,10 +48,10 @@ typedef enum {
|
||||
/*
|
||||
** Mask with 1 in all fast-access methods. A 1 in any of these bits
|
||||
** in the flag of a (meta)table means the metatable does not have the
|
||||
** corresponding metamethod field. (Bit 7 of the flag is used for
|
||||
** 'isrealasize'.)
|
||||
** corresponding metamethod field. (Bit 6 of the flag indicates that
|
||||
** the table is using the dummy node; bit 7 is used for 'isrealasize'.)
|
||||
*/
|
||||
#define maskflags (~(~0u << (TM_EQ + 1)))
|
||||
#define maskflags cast_byte(~(~0u << (TM_EQ + 1)))
|
||||
|
||||
|
||||
/*
|
||||
@@ -60,11 +60,12 @@ typedef enum {
|
||||
*/
|
||||
#define notm(tm) ttisnil(tm)
|
||||
|
||||
#define checknoTM(mt,e) ((mt) == NULL || (mt)->flags & (1u<<(e)))
|
||||
|
||||
#define gfasttm(g,et,e) ((et) == NULL ? NULL : \
|
||||
((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))
|
||||
#define gfasttm(g,mt,e) \
|
||||
(checknoTM(mt, e) ? NULL : luaT_gettm(mt, e, (g)->tmname[e]))
|
||||
|
||||
#define fasttm(l,et,e) gfasttm(G(l), et, e)
|
||||
#define fasttm(l,mt,e) gfasttm(G(l), mt, e)
|
||||
|
||||
#define ttypename(x) luaT_typenames_[(x) + 1]
|
||||
|
||||
@@ -80,8 +81,8 @@ LUAI_FUNC void luaT_init (lua_State *L);
|
||||
|
||||
LUAI_FUNC void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1,
|
||||
const TValue *p2, const TValue *p3);
|
||||
LUAI_FUNC void luaT_callTMres (lua_State *L, const TValue *f,
|
||||
const TValue *p1, const TValue *p2, StkId p3);
|
||||
LUAI_FUNC lu_byte 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);
|
||||
|
||||
244
lua.c
244
lua.c
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
#include "llimits.h"
|
||||
|
||||
|
||||
#if !defined(LUA_PROGNAME)
|
||||
@@ -89,14 +90,15 @@ static void print_usage (const char *badoption) {
|
||||
lua_writestringerror(
|
||||
"usage: %s [options] [script [args]]\n"
|
||||
"Available options are:\n"
|
||||
" -e stat execute string 'stat'\n"
|
||||
" -i enter interactive mode after executing 'script'\n"
|
||||
" -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"
|
||||
" -e stat execute string 'stat'\n"
|
||||
" -i enter interactive mode after executing 'script'\n"
|
||||
" -l mod require library 'mod' into global 'mod'\n"
|
||||
" -l g=mod require library 'mod' into global 'g'\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"
|
||||
,
|
||||
progname);
|
||||
}
|
||||
@@ -114,12 +116,13 @@ static void l_message (const char *pname, const char *msg) {
|
||||
|
||||
/*
|
||||
** Check whether 'status' is not OK and, if so, prints the error
|
||||
** message on the top of the stack. It assumes that the error object
|
||||
** is a string, as it was either generated by Lua or by 'msghandler'.
|
||||
** message on the top of the stack.
|
||||
*/
|
||||
static int report (lua_State *L, int status) {
|
||||
if (status != LUA_OK) {
|
||||
const char *msg = lua_tostring(L, -1);
|
||||
if (msg == NULL)
|
||||
msg = "(error message not a string)";
|
||||
l_message(progname, msg);
|
||||
lua_pop(L, 1); /* remove message */
|
||||
}
|
||||
@@ -176,10 +179,11 @@ static void print_version (void) {
|
||||
** to the script (everything after 'script') go to positive indices;
|
||||
** other arguments (before the script name) go to negative indices.
|
||||
** If there is no script name, assume interpreter's name as base.
|
||||
** (If there is no interpreter's name either, 'script' is -1, so
|
||||
** table sizes are zero.)
|
||||
*/
|
||||
static void createargtable (lua_State *L, char **argv, int argc, int script) {
|
||||
int i, narg;
|
||||
if (script == argc) script = 0; /* no script name? */
|
||||
narg = argc - (script + 1); /* number of positive indices */
|
||||
lua_createtable(L, narg, script + 1);
|
||||
for (i = 0; i < argc; i++) {
|
||||
@@ -207,16 +211,30 @@ static int dostring (lua_State *L, const char *s, const char *name) {
|
||||
|
||||
|
||||
/*
|
||||
** Calls 'require(name)' and stores the result in a global variable
|
||||
** with the given name.
|
||||
** Receives 'globname[=modname]' and runs 'globname = require(modname)'.
|
||||
** If there is no explicit modname and globname contains a '-', cut
|
||||
** the suffix after '-' (the "version") to make the global name.
|
||||
*/
|
||||
static int dolibrary (lua_State *L, const char *name) {
|
||||
static int dolibrary (lua_State *L, char *globname) {
|
||||
int status;
|
||||
char *suffix = NULL;
|
||||
char *modname = strchr(globname, '=');
|
||||
if (modname == NULL) { /* no explicit name? */
|
||||
modname = globname; /* module name is equal to global name */
|
||||
suffix = strchr(modname, *LUA_IGMARK); /* look for a suffix mark */
|
||||
}
|
||||
else {
|
||||
*modname = '\0'; /* global name ends here */
|
||||
modname++; /* module name starts after the '=' */
|
||||
}
|
||||
lua_getglobal(L, "require");
|
||||
lua_pushstring(L, name);
|
||||
status = docall(L, 1, 1); /* call 'require(name)' */
|
||||
if (status == LUA_OK)
|
||||
lua_setglobal(L, name); /* global[name] = require return */
|
||||
lua_pushstring(L, modname);
|
||||
status = docall(L, 1, 1); /* call 'require(modname)' */
|
||||
if (status == LUA_OK) {
|
||||
if (suffix != NULL) /* is there a suffix mark? */
|
||||
*suffix = '\0'; /* remove suffix from global name */
|
||||
lua_setglobal(L, globname); /* globname = require(modname) */
|
||||
}
|
||||
return report(L, status);
|
||||
}
|
||||
|
||||
@@ -261,14 +279,23 @@ static int handle_script (lua_State *L, char **argv) {
|
||||
|
||||
/*
|
||||
** Traverses all arguments from 'argv', returning a mask with those
|
||||
** needed before running any Lua code (or an error code if it finds
|
||||
** any invalid argument). 'first' returns the first not-handled argument
|
||||
** (either the script name or a bad argument in case of error).
|
||||
** needed before running any Lua code or an error code if it finds any
|
||||
** invalid argument. In case of error, 'first' is the index of the bad
|
||||
** argument. Otherwise, 'first' is -1 if there is no program name,
|
||||
** 0 if there is no script name, or the index of the script name.
|
||||
*/
|
||||
static int collectargs (char **argv, int *first) {
|
||||
int args = 0;
|
||||
int i;
|
||||
for (i = 1; argv[i] != NULL; i++) {
|
||||
if (argv[0] != NULL) { /* is there a program name? */
|
||||
if (argv[0][0]) /* not empty? */
|
||||
progname = argv[0]; /* save it */
|
||||
}
|
||||
else { /* no program name */
|
||||
*first = -1;
|
||||
return 0;
|
||||
}
|
||||
for (i = 1; argv[i] != NULL; i++) { /* handle arguments */
|
||||
*first = i;
|
||||
if (argv[i][0] != '-') /* not an option? */
|
||||
return args; /* stop handling options */
|
||||
@@ -309,7 +336,7 @@ static int collectargs (char **argv, int *first) {
|
||||
return has_error;
|
||||
}
|
||||
}
|
||||
*first = i; /* no script name */
|
||||
*first = 0; /* no script name */
|
||||
return args;
|
||||
}
|
||||
|
||||
@@ -327,7 +354,7 @@ static int runargs (lua_State *L, char **argv, int n) {
|
||||
switch (option) {
|
||||
case 'e': case 'l': {
|
||||
int status;
|
||||
const char *extra = argv[i] + 2; /* both options need an argument */
|
||||
char *extra = argv[i] + 2; /* both options need an argument */
|
||||
if (*extra == '\0') extra = argv[++i];
|
||||
lua_assert(extra != NULL);
|
||||
status = (option == 'e')
|
||||
@@ -405,32 +432,90 @@ static int handle_luainit (lua_State *L) {
|
||||
|
||||
|
||||
/*
|
||||
** lua_readline defines how to show a prompt and then read a line from
|
||||
** the standard input.
|
||||
** lua_saveline defines how to "save" a read line in a "history".
|
||||
** lua_freeline defines how to free a line read by lua_readline.
|
||||
** * lua_initreadline initializes the readline system.
|
||||
** * lua_readline defines how to show a prompt and then read a line from
|
||||
** the standard input.
|
||||
** * lua_saveline defines how to "save" a read line in a "history".
|
||||
** * lua_freeline defines how to free a line read by lua_readline.
|
||||
**
|
||||
** If lua_readline is defined, all of them should be defined.
|
||||
*/
|
||||
|
||||
#if !defined(lua_readline) /* { */
|
||||
|
||||
/* Code to use the readline library, either statically or dynamically linked */
|
||||
|
||||
/* pointer to 'readline' function (if any) */
|
||||
typedef char *(*l_readlineT) (const char *prompt);
|
||||
static l_readlineT l_readline = NULL;
|
||||
|
||||
/* pointer to 'add_history' function (if any) */
|
||||
typedef void (*l_addhistT) (const char *string);
|
||||
static l_addhistT l_addhist = NULL;
|
||||
|
||||
|
||||
static char *lua_readline (char *buff, const char *prompt) {
|
||||
if (l_readline != NULL) /* is there a 'readline'? */
|
||||
return (*l_readline)(prompt); /* use it */
|
||||
else { /* emulate 'readline' over 'buff' */
|
||||
fputs(prompt, stdout);
|
||||
fflush(stdout); /* show prompt */
|
||||
return fgets(buff, LUA_MAXINPUT, stdin); /* read line */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void lua_saveline (const char *line) {
|
||||
if (l_addhist != NULL) /* is there an 'add_history'? */
|
||||
(*l_addhist)(line); /* use it */
|
||||
/* else nothing to be done */
|
||||
}
|
||||
|
||||
|
||||
static void lua_freeline (char *line) {
|
||||
if (l_readline != NULL) /* is there a 'readline'? */
|
||||
free(line); /* free line created by it */
|
||||
/* else 'lua_readline' used an automatic buffer; nothing to free */
|
||||
}
|
||||
|
||||
|
||||
#if defined(LUA_USE_READLINE) /* { */
|
||||
|
||||
/* assume Lua will be linked with '-lreadline' */
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
#define lua_initreadline(L) ((void)L, rl_readline_name="lua")
|
||||
#define lua_readline(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))
|
||||
|
||||
#else /* }{ */
|
||||
static void lua_initreadline(lua_State *L) {
|
||||
UNUSED(L);
|
||||
rl_readline_name = "lua";
|
||||
l_readline = cast(l_readlineT, readline);
|
||||
l_addhist = cast(l_addhistT, add_history);
|
||||
}
|
||||
|
||||
#define lua_initreadline(L) ((void)L)
|
||||
#define lua_readline(L,b,p) \
|
||||
((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \
|
||||
fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */
|
||||
#define lua_saveline(L,line) { (void)L; (void)line; }
|
||||
#define lua_freeline(L,b) { (void)L; (void)b; }
|
||||
#elif defined(LUA_USE_DLOPEN) && defined(LUA_READLINELIB) /* }{ */
|
||||
|
||||
#endif /* } */
|
||||
/* try to load 'readline' dynamically */
|
||||
#include <dlfcn.h>
|
||||
|
||||
static void lua_initreadline (lua_State *L) {
|
||||
void *lib = dlopen(LUA_READLINELIB, RTLD_NOW | RTLD_LOCAL);
|
||||
if (lib == NULL)
|
||||
lua_warning(L, "library '" LUA_READLINELIB "' not found", 0);
|
||||
else {
|
||||
const char **name = cast(const char**, dlsym(lib, "rl_readline_name"));
|
||||
if (name != NULL)
|
||||
*name = "lua";
|
||||
l_readline = cast(l_readlineT, cast_func(dlsym(lib, "readline")));
|
||||
l_addhist = cast(l_addhistT, cast_func(dlsym(lib, "add_history")));
|
||||
}
|
||||
}
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
/* no readline; leave function pointers as NULL */
|
||||
#define lua_initreadline(L) cast(void, L)
|
||||
|
||||
#endif /* } */
|
||||
|
||||
#endif /* } */
|
||||
|
||||
@@ -464,10 +549,8 @@ static int incomplete (lua_State *L, int status) {
|
||||
if (status == LUA_ERRSYNTAX) {
|
||||
size_t lmsg;
|
||||
const char *msg = lua_tolstring(L, -1, &lmsg);
|
||||
if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) {
|
||||
lua_pop(L, 1);
|
||||
if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0; /* else... */
|
||||
}
|
||||
@@ -478,21 +561,17 @@ static int incomplete (lua_State *L, int status) {
|
||||
*/
|
||||
static int pushline (lua_State *L, int firstline) {
|
||||
char buffer[LUA_MAXINPUT];
|
||||
char *b = buffer;
|
||||
size_t l;
|
||||
const char *prmt = get_prompt(L, firstline);
|
||||
int readstatus = lua_readline(L, b, prmt);
|
||||
if (readstatus == 0)
|
||||
return 0; /* no input (prompt will be popped by caller) */
|
||||
char *b = lua_readline(buffer, prmt);
|
||||
lua_pop(L, 1); /* remove prompt */
|
||||
if (b == NULL)
|
||||
return 0; /* no input */
|
||||
l = strlen(b);
|
||||
if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
|
||||
b[--l] = '\0'; /* remove it */
|
||||
if (firstline && b[0] == '=') /* for compatibility with 5.2, ... */
|
||||
lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */
|
||||
else
|
||||
lua_pushlstring(L, b, l);
|
||||
lua_freeline(L, b);
|
||||
lua_pushlstring(L, b, l);
|
||||
lua_freeline(b);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -505,32 +584,44 @@ static int addreturn (lua_State *L) {
|
||||
const char *line = lua_tostring(L, -1); /* original line */
|
||||
const char *retline = lua_pushfstring(L, "return %s;", line);
|
||||
int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin");
|
||||
if (status == LUA_OK) {
|
||||
if (status == LUA_OK)
|
||||
lua_remove(L, -2); /* remove modified line */
|
||||
if (line[0] != '\0') /* non empty? */
|
||||
lua_saveline(L, line); /* keep history */
|
||||
}
|
||||
else
|
||||
lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static void checklocal (const char *line) {
|
||||
static const size_t szloc = sizeof("local") - 1;
|
||||
static const char space[] = " \t";
|
||||
line += strspn(line, space); /* skip spaces */
|
||||
if (strncmp(line, "local", szloc) == 0 && /* "local"? */
|
||||
strchr(space, *(line + szloc)) != NULL) { /* followed by a space? */
|
||||
lua_writestringerror("%s\n",
|
||||
"warning: locals do not survive across lines in interactive mode");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Read multiple lines until a complete Lua statement
|
||||
** Read multiple lines until a complete Lua statement or an error not
|
||||
** for an incomplete statement. Start with first line already read in
|
||||
** the stack.
|
||||
*/
|
||||
static int multiline (lua_State *L) {
|
||||
size_t len;
|
||||
const char *line = lua_tolstring(L, 1, &len); /* get first line */
|
||||
checklocal(line);
|
||||
for (;;) { /* repeat until gets a complete statement */
|
||||
size_t len;
|
||||
const char *line = lua_tolstring(L, 1, &len); /* get what it has */
|
||||
int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */
|
||||
if (!incomplete(L, status) || !pushline(L, 0)) {
|
||||
lua_saveline(L, line); /* keep history */
|
||||
return status; /* cannot or should not try to add continuation line */
|
||||
}
|
||||
if (!incomplete(L, status) || !pushline(L, 0))
|
||||
return status; /* should not or cannot try to add continuation line */
|
||||
lua_remove(L, -2); /* remove error message (from incomplete line) */
|
||||
lua_pushliteral(L, "\n"); /* add newline... */
|
||||
lua_insert(L, -2); /* ...between the two lines */
|
||||
lua_concat(L, 3); /* join them */
|
||||
line = lua_tolstring(L, 1, &len); /* get what is has */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,12 +633,16 @@ static int multiline (lua_State *L) {
|
||||
** in the top of the stack.
|
||||
*/
|
||||
static int loadline (lua_State *L) {
|
||||
const char *line;
|
||||
int status;
|
||||
lua_settop(L, 0);
|
||||
if (!pushline(L, 1))
|
||||
return -1; /* no input */
|
||||
if ((status = addreturn(L)) != LUA_OK) /* 'return ...' did not work? */
|
||||
status = multiline(L); /* try as command, maybe with continuation lines */
|
||||
line = lua_tostring(L, 1);
|
||||
if (line[0] != '\0') /* non empty? */
|
||||
lua_saveline(line); /* keep history */
|
||||
lua_remove(L, 1); /* remove line from the stack */
|
||||
lua_assert(lua_gettop(L) == 1);
|
||||
return status;
|
||||
@@ -592,6 +687,10 @@ static void doREPL (lua_State *L) {
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
#if !defined(luai_openlibs)
|
||||
#define luai_openlibs(L) luaL_openselectedlibs(L, ~0, 0)
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Main body of stand-alone interpreter (to be called in protected mode).
|
||||
@@ -602,8 +701,8 @@ static int pmain (lua_State *L) {
|
||||
char **argv = (char **)lua_touserdata(L, 2);
|
||||
int script;
|
||||
int args = collectargs(argv, &script);
|
||||
int optlim = (script > 0) ? script : argc; /* first argv not an option */
|
||||
luaL_checkversion(L); /* check that interpreter has correct version */
|
||||
if (argv[0] && argv[0][0]) progname = argv[0];
|
||||
if (args == has_error) { /* bad arg? */
|
||||
print_usage(argv[script]); /* 'script' has index of bad arg. */
|
||||
return 0;
|
||||
@@ -614,21 +713,23 @@ static int pmain (lua_State *L) {
|
||||
lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
|
||||
}
|
||||
luaL_openlibs(L); /* open standard libraries */
|
||||
luai_openlibs(L); /* open standard libraries */
|
||||
createargtable(L, argv, argc, script); /* create table 'arg' */
|
||||
lua_gc(L, LUA_GCGEN, 0, 0); /* GC in generational mode */
|
||||
lua_gc(L, LUA_GCRESTART); /* start GC... */
|
||||
lua_gc(L, LUA_GCGEN); /* ...in generational mode */
|
||||
if (!(args & has_E)) { /* no option '-E'? */
|
||||
if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */
|
||||
return 0; /* error running LUA_INIT */
|
||||
}
|
||||
if (!runargs(L, argv, script)) /* execute arguments -e and -l */
|
||||
if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */
|
||||
return 0; /* something failed */
|
||||
if (script < argc && /* execute main script (if there is one) */
|
||||
handle_script(L, argv + script) != LUA_OK)
|
||||
return 0;
|
||||
if (script > 0) { /* execute main script (if there is one) */
|
||||
if (handle_script(L, argv + script) != LUA_OK)
|
||||
return 0; /* interrupt in case of error */
|
||||
}
|
||||
if (args & has_i) /* -i option? */
|
||||
doREPL(L); /* do read-eval-print loop */
|
||||
else if (script == argc && !(args & (has_e | has_v))) { /* no arguments? */
|
||||
else if (script < 1 && !(args & (has_e | has_v))) { /* no active option? */
|
||||
if (lua_stdin_is_tty()) { /* running in interactive mode? */
|
||||
print_version();
|
||||
doREPL(L); /* do read-eval-print loop */
|
||||
@@ -647,6 +748,7 @@ int main (int argc, char **argv) {
|
||||
l_message(argv[0], "cannot create state: not enough memory");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
lua_gc(L, LUA_GCSTOP); /* stop GC while building state */
|
||||
lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */
|
||||
lua_pushinteger(L, argc); /* 1st argument */
|
||||
lua_pushlightuserdata(L, argv); /* 2nd argument */
|
||||
|
||||
108
lua.h
108
lua.h
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
** $Id: lua.h $
|
||||
** Lua - A Scripting Language
|
||||
** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
|
||||
** Lua.org, PUC-Rio, Brazil (www.lua.org)
|
||||
** See Copyright Notice at the end of this file
|
||||
*/
|
||||
|
||||
@@ -13,22 +13,21 @@
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
#include "luaconf.h"
|
||||
|
||||
|
||||
#define LUA_VERSION_MAJOR "5"
|
||||
#define LUA_VERSION_MINOR "4"
|
||||
#define LUA_VERSION_RELEASE "3"
|
||||
|
||||
#define LUA_VERSION_NUM 504
|
||||
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 0)
|
||||
|
||||
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
|
||||
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
|
||||
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2021 Lua.org, PUC-Rio"
|
||||
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2025 Lua.org, PUC-Rio"
|
||||
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
|
||||
|
||||
|
||||
#define LUA_VERSION_MAJOR_N 5
|
||||
#define LUA_VERSION_MINOR_N 5
|
||||
#define LUA_VERSION_RELEASE_N 0
|
||||
|
||||
#define LUA_VERSION_NUM (LUA_VERSION_MAJOR_N * 100 + LUA_VERSION_MINOR_N)
|
||||
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + LUA_VERSION_RELEASE_N)
|
||||
|
||||
|
||||
#include "luaconf.h"
|
||||
|
||||
|
||||
/* mark for precompiled code ('<esc>Lua') */
|
||||
#define LUA_SIGNATURE "\x1bLua"
|
||||
|
||||
@@ -81,9 +80,10 @@ typedef struct lua_State lua_State;
|
||||
|
||||
|
||||
/* predefined values in the registry */
|
||||
#define LUA_RIDX_MAINTHREAD 1
|
||||
/* index 1 is reserved for the reference mechanism */
|
||||
#define LUA_RIDX_GLOBALS 2
|
||||
#define LUA_RIDX_LAST LUA_RIDX_GLOBALS
|
||||
#define LUA_RIDX_MAINTHREAD 3
|
||||
#define LUA_RIDX_LAST 3
|
||||
|
||||
|
||||
/* type of numbers in Lua */
|
||||
@@ -131,6 +131,16 @@ typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
|
||||
typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont);
|
||||
|
||||
|
||||
/*
|
||||
** Type used by the debug API to collect debug information
|
||||
*/
|
||||
typedef struct lua_Debug lua_Debug;
|
||||
|
||||
|
||||
/*
|
||||
** Functions to be called by the debugger in specific events
|
||||
*/
|
||||
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
|
||||
|
||||
|
||||
/*
|
||||
@@ -150,10 +160,10 @@ extern const char lua_ident[];
|
||||
/*
|
||||
** state manipulation
|
||||
*/
|
||||
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
|
||||
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud, unsigned seed);
|
||||
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 int (lua_closethread) (lua_State *L, lua_State *from);
|
||||
|
||||
LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
|
||||
|
||||
@@ -234,6 +244,8 @@ LUA_API void (lua_pushnil) (lua_State *L);
|
||||
LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
|
||||
LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
|
||||
LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len);
|
||||
LUA_API const char *(lua_pushexternalstring) (lua_State *L,
|
||||
const char *s, size_t len, lua_Alloc falloc, void *ud);
|
||||
LUA_API const char *(lua_pushstring) (lua_State *L, const char *s);
|
||||
LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
|
||||
va_list argp);
|
||||
@@ -313,7 +325,7 @@ LUA_API void (lua_warning) (lua_State *L, const char *msg, int tocont);
|
||||
|
||||
|
||||
/*
|
||||
** garbage-collection function and options
|
||||
** garbage-collection options
|
||||
*/
|
||||
|
||||
#define LUA_GCSTOP 0
|
||||
@@ -322,11 +334,28 @@ LUA_API void (lua_warning) (lua_State *L, const char *msg, int tocont);
|
||||
#define LUA_GCCOUNT 3
|
||||
#define LUA_GCCOUNTB 4
|
||||
#define LUA_GCSTEP 5
|
||||
#define LUA_GCSETPAUSE 6
|
||||
#define LUA_GCSETSTEPMUL 7
|
||||
#define LUA_GCISRUNNING 9
|
||||
#define LUA_GCGEN 10
|
||||
#define LUA_GCINC 11
|
||||
#define LUA_GCISRUNNING 6
|
||||
#define LUA_GCGEN 7
|
||||
#define LUA_GCINC 8
|
||||
#define LUA_GCPARAM 9
|
||||
|
||||
|
||||
/*
|
||||
** garbage-collection parameters
|
||||
*/
|
||||
/* parameters for generational mode */
|
||||
#define LUA_GCPMINORMUL 0 /* control minor collections */
|
||||
#define LUA_GCPMAJORMINOR 1 /* control shift major->minor */
|
||||
#define LUA_GCPMINORMAJOR 2 /* control shift minor->major */
|
||||
|
||||
/* parameters for incremental mode */
|
||||
#define LUA_GCPPAUSE 3 /* size of pause between successive GCs */
|
||||
#define LUA_GCPSTEPMUL 4 /* GC "speed" */
|
||||
#define LUA_GCPSTEPSIZE 5 /* GC granularity */
|
||||
|
||||
/* number of parameters */
|
||||
#define LUA_GCPN 6
|
||||
|
||||
|
||||
LUA_API int (lua_gc) (lua_State *L, int what, ...);
|
||||
|
||||
@@ -342,7 +371,9 @@ LUA_API int (lua_next) (lua_State *L, int idx);
|
||||
LUA_API void (lua_concat) (lua_State *L, int n);
|
||||
LUA_API void (lua_len) (lua_State *L, int idx);
|
||||
|
||||
LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s);
|
||||
#define LUA_N2SBUFFSZ 64
|
||||
LUA_API unsigned (lua_numbertocstring) (lua_State *L, int idx, char *buff);
|
||||
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);
|
||||
@@ -413,7 +444,7 @@ LUA_API void (lua_closeslot) (lua_State *L, int idx);
|
||||
#define lua_getuservalue(L,idx) lua_getiuservalue(L,idx,1)
|
||||
#define lua_setuservalue(L,idx) lua_setiuservalue(L,idx,1)
|
||||
|
||||
#define LUA_NUMTAGS LUA_NUMTYPES
|
||||
#define lua_resetthread(L) lua_closethread(L,NULL)
|
||||
|
||||
/* }============================================================== */
|
||||
|
||||
@@ -442,12 +473,6 @@ LUA_API void (lua_closeslot) (lua_State *L, int idx);
|
||||
#define LUA_MASKLINE (1 << LUA_HOOKLINE)
|
||||
#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
|
||||
|
||||
typedef struct lua_Debug lua_Debug; /* activation record */
|
||||
|
||||
|
||||
/* Functions to be called by the debugger in specific events */
|
||||
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
|
||||
|
||||
|
||||
LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar);
|
||||
LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar);
|
||||
@@ -465,7 +490,6 @@ 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;
|
||||
@@ -480,9 +504,10 @@ struct lua_Debug {
|
||||
unsigned char nups; /* (u) number of upvalues */
|
||||
unsigned char nparams;/* (u) number of parameters */
|
||||
char isvararg; /* (u) */
|
||||
unsigned char extraargs; /* (t) number of extra arguments */
|
||||
char istailcall; /* (t) */
|
||||
unsigned short ftransfer; /* (r) index of first value transferred */
|
||||
unsigned short ntransfer; /* (r) number of transferred values */
|
||||
int ftransfer; /* (r) index of first value transferred */
|
||||
int ntransfer; /* (r) number of transferred values */
|
||||
char short_src[LUA_IDSIZE]; /* (S) */
|
||||
/* private part */
|
||||
struct CallInfo *i_ci; /* active function */
|
||||
@@ -491,8 +516,19 @@ struct lua_Debug {
|
||||
/* }====================================================================== */
|
||||
|
||||
|
||||
#define LUAI_TOSTRAUX(x) #x
|
||||
#define LUAI_TOSTR(x) LUAI_TOSTRAUX(x)
|
||||
|
||||
#define LUA_VERSION_MAJOR LUAI_TOSTR(LUA_VERSION_MAJOR_N)
|
||||
#define LUA_VERSION_MINOR LUAI_TOSTR(LUA_VERSION_MINOR_N)
|
||||
#define LUA_VERSION_RELEASE LUAI_TOSTR(LUA_VERSION_RELEASE_N)
|
||||
|
||||
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
|
||||
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright (C) 1994-2021 Lua.org, PUC-Rio.
|
||||
* Copyright (C) 1994-2025 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
|
||||
|
||||
75
luaconf.h
75
luaconf.h
@@ -58,15 +58,37 @@
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** When Posix DLL ('LUA_USE_DLOPEN') is enabled, the Lua stand-alone
|
||||
** application will try to dynamically link a 'readline' facility
|
||||
** for its REPL. In that case, LUA_READLINELIB is the name of the
|
||||
** library it will look for those facilities. If lua.c cannot open
|
||||
** the specified library, it will generate a warning and then run
|
||||
** without 'readline'. If that macro is not defined, lua.c will not
|
||||
** use 'readline'.
|
||||
*/
|
||||
#if defined(LUA_USE_LINUX)
|
||||
#define LUA_USE_POSIX
|
||||
#define LUA_USE_DLOPEN /* needs an extra library: -ldl */
|
||||
#define LUA_READLINELIB "libreadline.so"
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(LUA_USE_MACOSX)
|
||||
#define LUA_USE_POSIX
|
||||
#define LUA_USE_DLOPEN /* MacOS does not need -ldl */
|
||||
#define LUA_READLINELIB "libedit.dylib"
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(LUA_USE_IOS)
|
||||
#define LUA_USE_POSIX
|
||||
#define LUA_USE_DLOPEN
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(LUA_USE_C89) && defined(LUA_USE_POSIX)
|
||||
#error "Posix is not compatible with C89"
|
||||
#endif
|
||||
|
||||
|
||||
@@ -251,6 +273,15 @@
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** LUA_IGMARK is a mark to ignore all after it when building the
|
||||
** module name (e.g., used to build the luaopen_ function name).
|
||||
** Typically, the suffix after the mark is the module version,
|
||||
** as in "mod-v1.2.so".
|
||||
*/
|
||||
#define LUA_IGMARK "-"
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
@@ -324,6 +355,12 @@
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ LUA_COMPAT_GLOBAL avoids 'global' being a reserved word
|
||||
*/
|
||||
#define LUA_COMPAT_GLOBAL
|
||||
|
||||
|
||||
/*
|
||||
@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.3.
|
||||
** You can define it to get all options, or change specific options
|
||||
@@ -390,8 +427,13 @@
|
||||
@@ l_floatatt(x) corrects float attribute 'x' to the proper float type
|
||||
** by prefixing it with one of FLT/DBL/LDBL.
|
||||
@@ LUA_NUMBER_FRMLEN is the length modifier for writing floats.
|
||||
@@ LUA_NUMBER_FMT is the format for writing floats.
|
||||
@@ lua_number2str converts a float to a string.
|
||||
@@ LUA_NUMBER_FMT is the format for writing floats with the maximum
|
||||
** number of digits that respects tostring(tonumber(numeral)) == numeral.
|
||||
** (That would be floor(log10(2^n)), where n is the number of bits in
|
||||
** the float mantissa.)
|
||||
@@ LUA_NUMBER_FMT_N is the format for writing floats with the minimum
|
||||
** number of digits that ensures tonumber(tostring(number)) == number.
|
||||
** (That would be LUA_NUMBER_FMT+2.)
|
||||
@@ 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 numeral to a number.
|
||||
@@ -402,8 +444,6 @@
|
||||
|
||||
#define l_floor(x) (l_mathop(floor)(x))
|
||||
|
||||
#define lua_number2str(s,sz,n) \
|
||||
l_sprintf((s), sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)(n))
|
||||
|
||||
/*
|
||||
@@ lua_numbertointeger converts a float number with an integral value
|
||||
@@ -432,6 +472,7 @@
|
||||
|
||||
#define LUA_NUMBER_FRMLEN ""
|
||||
#define LUA_NUMBER_FMT "%.7g"
|
||||
#define LUA_NUMBER_FMT_N "%.9g"
|
||||
|
||||
#define l_mathop(op) op##f
|
||||
|
||||
@@ -448,6 +489,7 @@
|
||||
|
||||
#define LUA_NUMBER_FRMLEN "L"
|
||||
#define LUA_NUMBER_FMT "%.19Lg"
|
||||
#define LUA_NUMBER_FMT_N "%.21Lg"
|
||||
|
||||
#define l_mathop(op) op##l
|
||||
|
||||
@@ -462,7 +504,8 @@
|
||||
#define LUAI_UACNUMBER double
|
||||
|
||||
#define LUA_NUMBER_FRMLEN ""
|
||||
#define LUA_NUMBER_FMT "%.14g"
|
||||
#define LUA_NUMBER_FMT "%.15g"
|
||||
#define LUA_NUMBER_FMT_N "%.17g"
|
||||
|
||||
#define l_mathop(op) op
|
||||
|
||||
@@ -485,7 +528,6 @@
|
||||
@@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER.
|
||||
@@ LUA_MININTEGER is the minimum value for a LUA_INTEGER.
|
||||
@@ LUA_MAXUNSIGNED is the maximum value for a LUA_UNSIGNED.
|
||||
@@ LUA_UNSIGNEDBITS is the number of bits in a LUA_UNSIGNED.
|
||||
@@ lua_integer2str converts an integer to a string.
|
||||
*/
|
||||
|
||||
@@ -506,9 +548,6 @@
|
||||
#define LUA_UNSIGNED unsigned LUAI_UACINT
|
||||
|
||||
|
||||
#define LUA_UNSIGNEDBITS (sizeof(LUA_UNSIGNED) * CHAR_BIT)
|
||||
|
||||
|
||||
/* now the variable definitions */
|
||||
|
||||
#if LUA_INT_TYPE == LUA_INT_INT /* { int */
|
||||
@@ -711,10 +750,7 @@
|
||||
@@ LUA_USE_APICHECK turns on several consistency checks on the C API.
|
||||
** Define it as a help when debugging C code.
|
||||
*/
|
||||
#if defined(LUA_USE_APICHECK)
|
||||
#include <assert.h>
|
||||
#define luai_apicheck(l,e) assert(e)
|
||||
#endif
|
||||
/* #define LUA_USE_APICHECK */
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
@@ -731,13 +767,13 @@
|
||||
@@ LUAI_MAXSTACK limits the size of the Lua stack.
|
||||
** CHANGE it if you need a different limit. This limit is arbitrary;
|
||||
** its only purpose is to stop Lua from consuming unlimited stack
|
||||
** space (and to reserve some numbers for pseudo-indices).
|
||||
** (It must fit into max(size_t)/32.)
|
||||
** space and to reserve some numbers for pseudo-indices.
|
||||
** (It must fit into max(int)/2.)
|
||||
*/
|
||||
#if LUAI_IS32INT
|
||||
#if 1000000 < (INT_MAX / 2)
|
||||
#define LUAI_MAXSTACK 1000000
|
||||
#else
|
||||
#define LUAI_MAXSTACK 15000
|
||||
#define LUAI_MAXSTACK (INT_MAX / 2u)
|
||||
#endif
|
||||
|
||||
|
||||
@@ -751,14 +787,15 @@
|
||||
|
||||
/*
|
||||
@@ LUA_IDSIZE gives the maximum size for the description of the source
|
||||
@@ of a function in debug information.
|
||||
** of a function in debug information.
|
||||
** CHANGE it if you want a different size.
|
||||
*/
|
||||
#define LUA_IDSIZE 60
|
||||
|
||||
|
||||
/*
|
||||
@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system.
|
||||
@@ LUAL_BUFFERSIZE is the initial buffer size used by the lauxlib
|
||||
** buffer system.
|
||||
*/
|
||||
#define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number)))
|
||||
|
||||
|
||||
67
lualib.h
67
lualib.h
@@ -14,39 +14,52 @@
|
||||
/* version suffix for environment variable names */
|
||||
#define LUA_VERSUFFIX "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR
|
||||
|
||||
|
||||
#define LUA_GLIBK 1
|
||||
LUAMOD_API int (luaopen_base) (lua_State *L);
|
||||
|
||||
#define LUA_COLIBNAME "coroutine"
|
||||
LUAMOD_API int (luaopen_coroutine) (lua_State *L);
|
||||
|
||||
#define LUA_TABLIBNAME "table"
|
||||
LUAMOD_API int (luaopen_table) (lua_State *L);
|
||||
|
||||
#define LUA_IOLIBNAME "io"
|
||||
LUAMOD_API int (luaopen_io) (lua_State *L);
|
||||
|
||||
#define LUA_OSLIBNAME "os"
|
||||
LUAMOD_API int (luaopen_os) (lua_State *L);
|
||||
|
||||
#define LUA_STRLIBNAME "string"
|
||||
LUAMOD_API int (luaopen_string) (lua_State *L);
|
||||
|
||||
#define LUA_UTF8LIBNAME "utf8"
|
||||
LUAMOD_API int (luaopen_utf8) (lua_State *L);
|
||||
|
||||
#define LUA_MATHLIBNAME "math"
|
||||
LUAMOD_API int (luaopen_math) (lua_State *L);
|
||||
|
||||
#define LUA_DBLIBNAME "debug"
|
||||
LUAMOD_API int (luaopen_debug) (lua_State *L);
|
||||
|
||||
#define LUA_LOADLIBNAME "package"
|
||||
#define LUA_LOADLIBK (LUA_GLIBK << 1)
|
||||
LUAMOD_API int (luaopen_package) (lua_State *L);
|
||||
|
||||
|
||||
/* open all previous libraries */
|
||||
LUALIB_API void (luaL_openlibs) (lua_State *L);
|
||||
#define LUA_COLIBNAME "coroutine"
|
||||
#define LUA_COLIBK (LUA_LOADLIBK << 1)
|
||||
LUAMOD_API int (luaopen_coroutine) (lua_State *L);
|
||||
|
||||
#define LUA_DBLIBNAME "debug"
|
||||
#define LUA_DBLIBK (LUA_COLIBK << 1)
|
||||
LUAMOD_API int (luaopen_debug) (lua_State *L);
|
||||
|
||||
#define LUA_IOLIBNAME "io"
|
||||
#define LUA_IOLIBK (LUA_DBLIBK << 1)
|
||||
LUAMOD_API int (luaopen_io) (lua_State *L);
|
||||
|
||||
#define LUA_MATHLIBNAME "math"
|
||||
#define LUA_MATHLIBK (LUA_IOLIBK << 1)
|
||||
LUAMOD_API int (luaopen_math) (lua_State *L);
|
||||
|
||||
#define LUA_OSLIBNAME "os"
|
||||
#define LUA_OSLIBK (LUA_MATHLIBK << 1)
|
||||
LUAMOD_API int (luaopen_os) (lua_State *L);
|
||||
|
||||
#define LUA_STRLIBNAME "string"
|
||||
#define LUA_STRLIBK (LUA_OSLIBK << 1)
|
||||
LUAMOD_API int (luaopen_string) (lua_State *L);
|
||||
|
||||
#define LUA_TABLIBNAME "table"
|
||||
#define LUA_TABLIBK (LUA_STRLIBK << 1)
|
||||
LUAMOD_API int (luaopen_table) (lua_State *L);
|
||||
|
||||
#define LUA_UTF8LIBNAME "utf8"
|
||||
#define LUA_UTF8LIBK (LUA_TABLIBK << 1)
|
||||
LUAMOD_API int (luaopen_utf8) (lua_State *L);
|
||||
|
||||
|
||||
/* open selected libraries */
|
||||
LUALIB_API void (luaL_openselectedlibs) (lua_State *L, int load, int preload);
|
||||
|
||||
/* open all libraries */
|
||||
#define luaL_openlibs(L) luaL_openselectedlibs(L, ~0, 0)
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
256
lundump.c
256
lundump.c
@@ -21,6 +21,7 @@
|
||||
#include "lmem.h"
|
||||
#include "lobject.h"
|
||||
#include "lstring.h"
|
||||
#include "ltable.h"
|
||||
#include "lundump.h"
|
||||
#include "lzio.h"
|
||||
|
||||
@@ -34,6 +35,10 @@ typedef struct {
|
||||
lua_State *L;
|
||||
ZIO *Z;
|
||||
const char *name;
|
||||
Table *h; /* list for string reuse */
|
||||
size_t offset; /* current position relative to beginning of dump */
|
||||
lua_Unsigned nstr; /* number of strings in the list */
|
||||
lu_byte fixed; /* dump is fixed in memory */
|
||||
} LoadState;
|
||||
|
||||
|
||||
@@ -47,11 +52,33 @@ static l_noret error (LoadState *S, const char *why) {
|
||||
** All high-level loads go through loadVector; you can change it to
|
||||
** adapt to the endianness of the input
|
||||
*/
|
||||
#define loadVector(S,b,n) loadBlock(S,b,(n)*sizeof((b)[0]))
|
||||
#define loadVector(S,b,n) loadBlock(S,b,cast_sizet(n)*sizeof((b)[0]))
|
||||
|
||||
static void loadBlock (LoadState *S, void *b, size_t size) {
|
||||
if (luaZ_read(S->Z, b, size) != 0)
|
||||
error(S, "truncated chunk");
|
||||
S->offset += size;
|
||||
}
|
||||
|
||||
|
||||
static void loadAlign (LoadState *S, unsigned align) {
|
||||
unsigned padding = align - cast_uint(S->offset % align);
|
||||
if (padding < align) { /* (padding == align) means no padding */
|
||||
lua_Integer paddingContent;
|
||||
loadBlock(S, &paddingContent, padding);
|
||||
lua_assert(S->offset % align == 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define getaddr(S,n,t) cast(t *, getaddr_(S,cast_sizet(n) * sizeof(t)))
|
||||
|
||||
static const void *getaddr_ (LoadState *S, size_t size) {
|
||||
const void *block = luaZ_getaddr(S->Z, size);
|
||||
S->offset += size;
|
||||
if (block == NULL)
|
||||
error(S, "truncated fixed buffer");
|
||||
return block;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,34 +89,36 @@ static lu_byte loadByte (LoadState *S) {
|
||||
int b = zgetc(S->Z);
|
||||
if (b == EOZ)
|
||||
error(S, "truncated chunk");
|
||||
S->offset++;
|
||||
return cast_byte(b);
|
||||
}
|
||||
|
||||
|
||||
static size_t loadUnsigned (LoadState *S, size_t limit) {
|
||||
size_t x = 0;
|
||||
static lua_Unsigned loadVarint (LoadState *S, lua_Unsigned limit) {
|
||||
lua_Unsigned x = 0;
|
||||
int b;
|
||||
limit >>= 7;
|
||||
do {
|
||||
b = loadByte(S);
|
||||
if (x >= limit)
|
||||
if (x > limit)
|
||||
error(S, "integer overflow");
|
||||
x = (x << 7) | (b & 0x7f);
|
||||
} while ((b & 0x80) == 0);
|
||||
} while ((b & 0x80) != 0);
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
static size_t loadSize (LoadState *S) {
|
||||
return loadUnsigned(S, ~(size_t)0);
|
||||
return loadVarint(S, MAX_SIZE);
|
||||
}
|
||||
|
||||
|
||||
static int loadInt (LoadState *S) {
|
||||
return cast_int(loadUnsigned(S, INT_MAX));
|
||||
return cast_int(loadVarint(S, cast_sizet(INT_MAX)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
static lua_Number loadNumber (LoadState *S) {
|
||||
lua_Number x;
|
||||
loadVar(S, x);
|
||||
@@ -98,58 +127,79 @@ static lua_Number loadNumber (LoadState *S) {
|
||||
|
||||
|
||||
static lua_Integer loadInteger (LoadState *S) {
|
||||
lua_Integer x;
|
||||
loadVar(S, x);
|
||||
return x;
|
||||
lua_Unsigned cx = loadVarint(S, LUA_MAXUNSIGNED);
|
||||
/* decode unsigned to signed */
|
||||
if ((cx & 1) != 0)
|
||||
return l_castU2S(~(cx >> 1));
|
||||
else
|
||||
return l_castU2S(cx >> 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Load a nullable string into prototype 'p'.
|
||||
** Load a nullable string into slot 'sl' from prototype 'p'. The
|
||||
** assignment to the slot and the barrier must be performed before any
|
||||
** possible GC activity, to anchor the string. (Both 'loadVector' and
|
||||
** 'luaH_setint' can call the GC.)
|
||||
*/
|
||||
static TString *loadStringN (LoadState *S, Proto *p) {
|
||||
static void loadString (LoadState *S, Proto *p, TString **sl) {
|
||||
lua_State *L = S->L;
|
||||
TString *ts;
|
||||
TValue sv;
|
||||
size_t size = loadSize(S);
|
||||
if (size == 0) /* no string? */
|
||||
return NULL;
|
||||
else if (--size <= LUAI_MAXSHORTLEN) { /* short string? */
|
||||
char buff[LUAI_MAXSHORTLEN];
|
||||
loadVector(S, buff, size); /* load string into buffer */
|
||||
ts = luaS_newlstr(L, buff, size); /* create string */
|
||||
if (size == 0) { /* no string? */
|
||||
lua_assert(*sl == NULL); /* must be prefilled */
|
||||
return;
|
||||
}
|
||||
else { /* long string */
|
||||
ts = luaS_createlngstrobj(L, size); /* create string */
|
||||
setsvalue2s(L, L->top, ts); /* anchor it ('loadVector' can GC) */
|
||||
luaD_inctop(L);
|
||||
loadVector(S, getstr(ts), size); /* load directly in final place */
|
||||
L->top--; /* pop string */
|
||||
else if (size == 1) { /* previously saved string? */
|
||||
lua_Unsigned idx = loadVarint(S, LUA_MAXUNSIGNED); /* get its index */
|
||||
TValue stv;
|
||||
if (novariant(luaH_getint(S->h, l_castU2S(idx), &stv)) != LUA_TSTRING)
|
||||
error(S, "invalid string index");
|
||||
*sl = ts = tsvalue(&stv); /* get its value */
|
||||
luaC_objbarrier(L, p, ts);
|
||||
return; /* do not save it again */
|
||||
}
|
||||
luaC_objbarrier(L, p, ts);
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Load a non-nullable string into prototype 'p'.
|
||||
*/
|
||||
static TString *loadString (LoadState *S, Proto *p) {
|
||||
TString *st = loadStringN(S, p);
|
||||
if (st == NULL)
|
||||
error(S, "bad format for constant string");
|
||||
return st;
|
||||
else if ((size -= 2) <= LUAI_MAXSHORTLEN) { /* short string? */
|
||||
char buff[LUAI_MAXSHORTLEN + 1]; /* extra space for '\0' */
|
||||
loadVector(S, buff, size + 1); /* load string into buffer */
|
||||
*sl = ts = luaS_newlstr(L, buff, size); /* create string */
|
||||
luaC_objbarrier(L, p, ts);
|
||||
}
|
||||
else if (S->fixed) { /* for a fixed buffer, use a fixed string */
|
||||
const char *s = getaddr(S, size + 1, char); /* get content address */
|
||||
*sl = ts = luaS_newextlstr(L, s, size, NULL, NULL);
|
||||
luaC_objbarrier(L, p, ts);
|
||||
}
|
||||
else { /* create internal copy */
|
||||
*sl = ts = luaS_createlngstrobj(L, size); /* create string */
|
||||
luaC_objbarrier(L, p, ts);
|
||||
loadVector(S, getlngstr(ts), size + 1); /* load directly in final place */
|
||||
}
|
||||
/* add string to list of saved strings */
|
||||
S->nstr++;
|
||||
setsvalue(L, &sv, ts);
|
||||
luaH_setint(L, S->h, l_castU2S(S->nstr), &sv);
|
||||
luaC_objbarrierback(L, obj2gco(S->h), ts);
|
||||
}
|
||||
|
||||
|
||||
static void loadCode (LoadState *S, Proto *f) {
|
||||
int n = loadInt(S);
|
||||
f->code = luaM_newvectorchecked(S->L, n, Instruction);
|
||||
f->sizecode = n;
|
||||
loadVector(S, f->code, n);
|
||||
loadAlign(S, sizeof(f->code[0]));
|
||||
if (S->fixed) {
|
||||
f->code = getaddr(S, n, Instruction);
|
||||
f->sizecode = n;
|
||||
}
|
||||
else {
|
||||
f->code = luaM_newvectorchecked(S->L, n, Instruction);
|
||||
f->sizecode = n;
|
||||
loadVector(S, f->code, n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void loadFunction(LoadState *S, Proto *f, TString *psource);
|
||||
static void loadFunction(LoadState *S, Proto *f);
|
||||
|
||||
|
||||
static void loadConstants (LoadState *S, Proto *f) {
|
||||
@@ -179,10 +229,16 @@ static void loadConstants (LoadState *S, Proto *f) {
|
||||
setivalue(o, loadInteger(S));
|
||||
break;
|
||||
case LUA_VSHRSTR:
|
||||
case LUA_VLNGSTR:
|
||||
setsvalue2n(S->L, o, loadString(S, f));
|
||||
case LUA_VLNGSTR: {
|
||||
lua_assert(f->source == NULL);
|
||||
loadString(S, f, &f->source); /* use 'source' to anchor string */
|
||||
if (f->source == NULL)
|
||||
error(S, "bad format for constant string");
|
||||
setsvalue2n(S->L, o, f->source); /* save it in the right place */
|
||||
f->source = NULL;
|
||||
break;
|
||||
default: lua_assert(0);
|
||||
}
|
||||
default: error(S, "invalid constant");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,7 +254,7 @@ static void loadProtos (LoadState *S, Proto *f) {
|
||||
for (i = 0; i < n; i++) {
|
||||
f->p[i] = luaF_newproto(S->L);
|
||||
luaC_objbarrier(S->L, f, f->p[i]);
|
||||
loadFunction(S, f->p[i], f->source);
|
||||
loadFunction(S, f->p[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,8 +266,8 @@ static void loadProtos (LoadState *S, Proto *f) {
|
||||
** in that case all prototypes must be consistent for the GC.
|
||||
*/
|
||||
static void loadUpvalues (LoadState *S, Proto *f) {
|
||||
int i, n;
|
||||
n = loadInt(S);
|
||||
int i;
|
||||
int n = loadInt(S);
|
||||
f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc);
|
||||
f->sizeupvalues = n;
|
||||
for (i = 0; i < n; i++) /* make array valid for GC */
|
||||
@@ -225,17 +281,29 @@ static void loadUpvalues (LoadState *S, Proto *f) {
|
||||
|
||||
|
||||
static void loadDebug (LoadState *S, Proto *f) {
|
||||
int i, n;
|
||||
int i;
|
||||
int n = loadInt(S);
|
||||
if (S->fixed) {
|
||||
f->lineinfo = getaddr(S, n, ls_byte);
|
||||
f->sizelineinfo = n;
|
||||
}
|
||||
else {
|
||||
f->lineinfo = luaM_newvectorchecked(S->L, n, ls_byte);
|
||||
f->sizelineinfo = n;
|
||||
loadVector(S, f->lineinfo, n);
|
||||
}
|
||||
n = loadInt(S);
|
||||
f->lineinfo = luaM_newvectorchecked(S->L, n, ls_byte);
|
||||
f->sizelineinfo = n;
|
||||
loadVector(S, f->lineinfo, n);
|
||||
n = loadInt(S);
|
||||
f->abslineinfo = luaM_newvectorchecked(S->L, n, AbsLineInfo);
|
||||
f->sizeabslineinfo = n;
|
||||
for (i = 0; i < n; i++) {
|
||||
f->abslineinfo[i].pc = loadInt(S);
|
||||
f->abslineinfo[i].line = loadInt(S);
|
||||
if (n > 0) {
|
||||
loadAlign(S, sizeof(int));
|
||||
if (S->fixed) {
|
||||
f->abslineinfo = getaddr(S, n, AbsLineInfo);
|
||||
f->sizeabslineinfo = n;
|
||||
}
|
||||
else {
|
||||
f->abslineinfo = luaM_newvectorchecked(S->L, n, AbsLineInfo);
|
||||
f->sizeabslineinfo = n;
|
||||
loadVector(S, f->abslineinfo, n);
|
||||
}
|
||||
}
|
||||
n = loadInt(S);
|
||||
f->locvars = luaM_newvectorchecked(S->L, n, LocVar);
|
||||
@@ -243,29 +311,31 @@ static void loadDebug (LoadState *S, Proto *f) {
|
||||
for (i = 0; i < n; i++)
|
||||
f->locvars[i].varname = NULL;
|
||||
for (i = 0; i < n; i++) {
|
||||
f->locvars[i].varname = loadStringN(S, f);
|
||||
loadString(S, f, &f->locvars[i].varname);
|
||||
f->locvars[i].startpc = loadInt(S);
|
||||
f->locvars[i].endpc = loadInt(S);
|
||||
}
|
||||
n = loadInt(S);
|
||||
if (n != 0) /* does it have debug information? */
|
||||
n = f->sizeupvalues; /* must be this many */
|
||||
for (i = 0; i < n; i++)
|
||||
f->upvalues[i].name = loadStringN(S, f);
|
||||
loadString(S, f, &f->upvalues[i].name);
|
||||
}
|
||||
|
||||
|
||||
static void loadFunction (LoadState *S, Proto *f, TString *psource) {
|
||||
f->source = loadStringN(S, f);
|
||||
if (f->source == NULL) /* no source in dump? */
|
||||
f->source = psource; /* reuse parent's source */
|
||||
static void loadFunction (LoadState *S, Proto *f) {
|
||||
f->linedefined = loadInt(S);
|
||||
f->lastlinedefined = loadInt(S);
|
||||
f->numparams = loadByte(S);
|
||||
f->is_vararg = loadByte(S);
|
||||
f->flag = loadByte(S) & PF_ISVARARG; /* get only the meaningful flags */
|
||||
if (S->fixed)
|
||||
f->flag |= PF_FIXED; /* signal that code is fixed */
|
||||
f->maxstacksize = loadByte(S);
|
||||
loadCode(S, f);
|
||||
loadConstants(S, f);
|
||||
loadUpvalues(S, f);
|
||||
loadProtos(S, f);
|
||||
loadString(S, f, &f->source);
|
||||
loadDebug(S, f);
|
||||
}
|
||||
|
||||
@@ -279,13 +349,29 @@ 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", tname));
|
||||
static l_noret numerror (LoadState *S, const char *what, const char *tname) {
|
||||
const char *msg = luaO_pushfstring(S->L, "%s %s mismatch", tname, what);
|
||||
error(S, msg);
|
||||
}
|
||||
|
||||
|
||||
#define checksize(S,t) fchecksize(S,sizeof(t),#t)
|
||||
static void checknumsize (LoadState *S, int size, const char *tname) {
|
||||
if (size != loadByte(S))
|
||||
numerror(S, "size", tname);
|
||||
}
|
||||
|
||||
|
||||
static void checknumformat (LoadState *S, int eq, const char *tname) {
|
||||
if (!eq)
|
||||
numerror(S, "format", tname);
|
||||
}
|
||||
|
||||
|
||||
#define checknum(S,tvar,value,tname) \
|
||||
{ tvar i; checknumsize(S, sizeof(i), tname); \
|
||||
loadVar(S, i); \
|
||||
checknumformat(S, i == value, tname); }
|
||||
|
||||
|
||||
static void checkHeader (LoadState *S) {
|
||||
/* skip 1st char (already read and checked) */
|
||||
@@ -295,39 +381,43 @@ static void checkHeader (LoadState *S) {
|
||||
if (loadByte(S) != LUAC_FORMAT)
|
||||
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, "integer format mismatch");
|
||||
if (loadNumber(S) != LUAC_NUM)
|
||||
error(S, "float format mismatch");
|
||||
checknum(S, int, LUAC_INT, "int");
|
||||
checknum(S, Instruction, LUAC_INST, "instruction");
|
||||
checknum(S, lua_Integer, LUAC_INT, "Lua integer");
|
||||
checknum(S, lua_Number, LUAC_NUM, "Lua number");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Load precompiled chunk.
|
||||
*/
|
||||
LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) {
|
||||
LClosure *luaU_undump (lua_State *L, ZIO *Z, const char *name, int fixed) {
|
||||
LoadState S;
|
||||
LClosure *cl;
|
||||
if (*name == '@' || *name == '=')
|
||||
S.name = name + 1;
|
||||
name = name + 1;
|
||||
else if (*name == LUA_SIGNATURE[0])
|
||||
S.name = "binary string";
|
||||
else
|
||||
S.name = name;
|
||||
name = "binary string";
|
||||
S.name = name;
|
||||
S.L = L;
|
||||
S.Z = Z;
|
||||
S.fixed = cast_byte(fixed);
|
||||
S.offset = 1; /* fist byte was already read */
|
||||
checkHeader(&S);
|
||||
cl = luaF_newLclosure(L, loadByte(&S));
|
||||
setclLvalue2s(L, L->top, cl);
|
||||
setclLvalue2s(L, L->top.p, cl);
|
||||
luaD_inctop(L);
|
||||
S.h = luaH_new(L); /* create list of saved strings */
|
||||
S.nstr = 0;
|
||||
sethvalue2s(L, L->top.p, S.h); /* anchor it */
|
||||
luaD_inctop(L);
|
||||
cl->p = luaF_newproto(L);
|
||||
luaC_objbarrier(L, cl, cl->p);
|
||||
loadFunction(&S, cl->p, NULL);
|
||||
lua_assert(cl->nupvalues == cl->p->sizeupvalues);
|
||||
loadFunction(&S, cl->p);
|
||||
if (cl->nupvalues != cl->p->sizeupvalues)
|
||||
error(&S, "corrupted chunk");
|
||||
luai_verifycode(L, cl->p);
|
||||
L->top.p--; /* pop table */
|
||||
return cl;
|
||||
}
|
||||
|
||||
|
||||
14
lundump.h
14
lundump.h
@@ -7,6 +7,8 @@
|
||||
#ifndef lundump_h
|
||||
#define lundump_h
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "llimits.h"
|
||||
#include "lobject.h"
|
||||
#include "lzio.h"
|
||||
@@ -15,19 +17,21 @@
|
||||
/* data to catch conversion errors */
|
||||
#define LUAC_DATA "\x19\x93\r\n\x1a\n"
|
||||
|
||||
#define LUAC_INT 0x5678
|
||||
#define LUAC_NUM cast_num(370.5)
|
||||
#define LUAC_INT -0x5678
|
||||
#define LUAC_INST 0x12345678
|
||||
#define LUAC_NUM cast_num(-370.5)
|
||||
|
||||
/*
|
||||
** Encode major-minor version in one byte, one nibble for each
|
||||
*/
|
||||
#define MYINT(s) (s[0]-'0') /* assume one-digit numerals */
|
||||
#define LUAC_VERSION (MYINT(LUA_VERSION_MAJOR)*16+MYINT(LUA_VERSION_MINOR))
|
||||
#define LUAC_VERSION (LUA_VERSION_MAJOR_N*16+LUA_VERSION_MINOR_N)
|
||||
|
||||
#define LUAC_FORMAT 0 /* this is the official format */
|
||||
|
||||
|
||||
/* load one chunk; from lundump.c */
|
||||
LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name);
|
||||
LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name,
|
||||
int fixed);
|
||||
|
||||
/* dump one chunk; from ldump.c */
|
||||
LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w,
|
||||
|
||||
116
lutf8lib.c
116
lutf8lib.c
@@ -19,23 +19,19 @@
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
#include "llimits.h"
|
||||
|
||||
|
||||
#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 MSGInvalid "invalid UTF-8 code"
|
||||
|
||||
|
||||
#define iscont(p) ((*(p) & 0xC0) == 0x80)
|
||||
#define iscont(c) (((c) & 0xC0) == 0x80)
|
||||
#define iscontp(p) iscont(*(p))
|
||||
|
||||
|
||||
/* from strlib */
|
||||
@@ -54,22 +50,22 @@ static lua_Integer u_posrelat (lua_Integer pos, size_t len) {
|
||||
** entry forces an error for non-ascii bytes with no continuation
|
||||
** bytes (count == 0).
|
||||
*/
|
||||
static const char *utf8_decode (const char *s, utfint *val, int strict) {
|
||||
static const utfint limits[] =
|
||||
{~(utfint)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u};
|
||||
static const char *utf8_decode (const char *s, l_uint32 *val, int strict) {
|
||||
static const l_uint32 limits[] =
|
||||
{~(l_uint32)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u};
|
||||
unsigned int c = (unsigned char)s[0];
|
||||
utfint res = 0; /* final result */
|
||||
l_uint32 res = 0; /* final result */
|
||||
if (c < 0x80) /* ascii? */
|
||||
res = c;
|
||||
else {
|
||||
int count = 0; /* to count number of continuation bytes */
|
||||
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? */
|
||||
if (!iscont(cc)) /* not a continuation byte? */
|
||||
return NULL; /* invalid byte sequence */
|
||||
res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
|
||||
}
|
||||
res |= ((utfint)(c & 0x7F) << (count * 5)); /* add first byte */
|
||||
res |= ((l_uint32)(c & 0x7F) << (count * 5)); /* add first byte */
|
||||
if (count > 5 || res > MAXUTF || res < limits[count])
|
||||
return NULL; /* invalid byte sequence */
|
||||
s += count; /* skip continuation bytes read */
|
||||
@@ -107,7 +103,7 @@ static int utflen (lua_State *L) {
|
||||
lua_pushinteger(L, posi + 1); /* ... and current position */
|
||||
return 2;
|
||||
}
|
||||
posi = s1 - s;
|
||||
posi = ct_diff2S(s1 - s);
|
||||
n++;
|
||||
}
|
||||
lua_pushinteger(L, n);
|
||||
@@ -137,11 +133,11 @@ static int codepoint (lua_State *L) {
|
||||
n = 0; /* count the number of returns */
|
||||
se = s + pose; /* string end */
|
||||
for (s += posi - 1; s < se;) {
|
||||
utfint code;
|
||||
l_uint32 code;
|
||||
s = utf8_decode(s, &code, !lax);
|
||||
if (s == NULL)
|
||||
return luaL_error(L, "invalid UTF-8 code");
|
||||
lua_pushinteger(L, code);
|
||||
return luaL_error(L, MSGInvalid);
|
||||
lua_pushinteger(L, l_castU2S(code));
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
@@ -177,69 +173,74 @@ static int utfchar (lua_State *L) {
|
||||
|
||||
|
||||
/*
|
||||
** offset(s, n, [i]) -> index where n-th character counting from
|
||||
** position 'i' starts; 0 means character at 'i'.
|
||||
** offset(s, n, [i]) -> indices where n-th character counting from
|
||||
** position 'i' starts and ends; 0 means character at 'i'.
|
||||
*/
|
||||
static int byteoffset (lua_State *L) {
|
||||
size_t len;
|
||||
const char *s = luaL_checklstring(L, 1, &len);
|
||||
lua_Integer n = luaL_checkinteger(L, 2);
|
||||
lua_Integer posi = (n >= 0) ? 1 : len + 1;
|
||||
lua_Integer posi = (n >= 0) ? 1 : cast_st2S(len) + 1;
|
||||
posi = u_posrelat(luaL_optinteger(L, 3, posi), len);
|
||||
luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 3,
|
||||
"position out of bounds");
|
||||
if (n == 0) {
|
||||
/* find beginning of current byte sequence */
|
||||
while (posi > 0 && iscont(s + posi)) posi--;
|
||||
while (posi > 0 && iscontp(s + posi)) posi--;
|
||||
}
|
||||
else {
|
||||
if (iscont(s + posi))
|
||||
if (iscontp(s + posi))
|
||||
return luaL_error(L, "initial position is a continuation byte");
|
||||
if (n < 0) {
|
||||
while (n < 0 && posi > 0) { /* move back */
|
||||
do { /* find beginning of previous character */
|
||||
posi--;
|
||||
} while (posi > 0 && iscont(s + posi));
|
||||
n++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
n--; /* do not move for 1st character */
|
||||
while (n > 0 && posi < (lua_Integer)len) {
|
||||
do { /* find beginning of next character */
|
||||
posi++;
|
||||
} while (iscont(s + posi)); /* (cannot pass final '\0') */
|
||||
n--;
|
||||
}
|
||||
}
|
||||
while (n < 0 && posi > 0) { /* move back */
|
||||
do { /* find beginning of previous character */
|
||||
posi--;
|
||||
} while (posi > 0 && iscontp(s + posi));
|
||||
n++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
n--; /* do not move for 1st character */
|
||||
while (n > 0 && posi < (lua_Integer)len) {
|
||||
do { /* find beginning of next character */
|
||||
posi++;
|
||||
} while (iscontp(s + posi)); /* (cannot pass final '\0') */
|
||||
n--;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (n == 0) /* did it find given character? */
|
||||
lua_pushinteger(L, posi + 1);
|
||||
else /* no such character */
|
||||
if (n != 0) { /* did not find given character? */
|
||||
luaL_pushfail(L);
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
lua_pushinteger(L, posi + 1); /* initial position */
|
||||
if ((s[posi] & 0x80) != 0) { /* multi-byte character? */
|
||||
do {
|
||||
posi++;
|
||||
} while (iscontp(s + posi + 1)); /* skip to final byte */
|
||||
}
|
||||
/* else one-byte character: final position is the initial one */
|
||||
lua_pushinteger(L, posi + 1); /* 'posi' now is the final position */
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
if (n < 0) /* first iteration? */
|
||||
n = 0; /* start from here */
|
||||
else if (n < (lua_Integer)len) {
|
||||
n++; /* skip current byte */
|
||||
while (iscont(s + n)) n++; /* and its continuations */
|
||||
lua_Unsigned n = (lua_Unsigned)lua_tointeger(L, 2);
|
||||
if (n < len) {
|
||||
while (iscontp(s + n)) n++; /* go to next character */
|
||||
}
|
||||
if (n >= (lua_Integer)len)
|
||||
if (n >= len) /* (also handles original 'n' being negative) */
|
||||
return 0; /* no more codepoints */
|
||||
else {
|
||||
utfint code;
|
||||
l_uint32 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);
|
||||
if (next == NULL || iscontp(next))
|
||||
return luaL_error(L, MSGInvalid);
|
||||
lua_pushinteger(L, l_castU2S(n + 1));
|
||||
lua_pushinteger(L, l_castU2S(code));
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
@@ -256,7 +257,8 @@ static int iter_auxlax (lua_State *L) {
|
||||
|
||||
static int iter_codes (lua_State *L) {
|
||||
int lax = lua_toboolean(L, 2);
|
||||
luaL_checkstring(L, 1);
|
||||
const char *s = luaL_checkstring(L, 1);
|
||||
luaL_argcheck(L, !iscontp(s), 1, MSGInvalid);
|
||||
lua_pushcfunction(L, lax ? iter_auxlax : iter_auxstrict);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushinteger(L, 0);
|
||||
|
||||
50
lvm.h
50
lvm.h
@@ -43,7 +43,7 @@
|
||||
typedef enum {
|
||||
F2Ieq, /* no rounding; accepts only integral values */
|
||||
F2Ifloor, /* takes the floor of the number */
|
||||
F2Iceil /* takes the ceil of the number */
|
||||
F2Iceil /* takes the ceiling of the number */
|
||||
} F2Imod;
|
||||
|
||||
|
||||
@@ -76,40 +76,40 @@ typedef enum {
|
||||
|
||||
|
||||
/*
|
||||
** fast track for 'gettable': if 't' is a table and 't[k]' is present,
|
||||
** return 1 with 'slot' pointing to 't[k]' (position of final result).
|
||||
** Otherwise, return 0 (meaning it will have to check metamethod)
|
||||
** with 'slot' pointing to an empty 't[k]' (if 't' is a table) or NULL
|
||||
** (otherwise). 'f' is the raw get function to use.
|
||||
** fast track for 'gettable'
|
||||
*/
|
||||
#define luaV_fastget(L,t,k,slot,f) \
|
||||
(!ttistable(t) \
|
||||
? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \
|
||||
: (slot = f(hvalue(t), k), /* else, do raw access */ \
|
||||
!isempty(slot))) /* result not empty? */
|
||||
#define luaV_fastget(t,k,res,f, tag) \
|
||||
(tag = (!ttistable(t) ? LUA_VNOTABLE : f(hvalue(t), k, res)))
|
||||
|
||||
|
||||
/*
|
||||
** Special case of 'luaV_fastget' for integers, inlining the fast case
|
||||
** of 'luaH_getint'.
|
||||
*/
|
||||
#define luaV_fastgeti(L,t,k,slot) \
|
||||
(!ttistable(t) \
|
||||
? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \
|
||||
: (slot = (l_castS2U(k) - 1u < hvalue(t)->alimit) \
|
||||
? &hvalue(t)->array[k - 1] : luaH_getint(hvalue(t), k), \
|
||||
!isempty(slot))) /* result not empty? */
|
||||
#define luaV_fastgeti(t,k,res,tag) \
|
||||
if (!ttistable(t)) tag = LUA_VNOTABLE; \
|
||||
else { luaH_fastgeti(hvalue(t), k, res, tag); }
|
||||
|
||||
|
||||
#define luaV_fastset(t,k,val,hres,f) \
|
||||
(hres = (!ttistable(t) ? HNOTATABLE : f(hvalue(t), k, val)))
|
||||
|
||||
#define luaV_fastseti(t,k,val,hres) \
|
||||
if (!ttistable(t)) hres = HNOTATABLE; \
|
||||
else { luaH_fastseti(hvalue(t), k, val, hres); }
|
||||
|
||||
|
||||
/*
|
||||
** Finish a fast set operation (when fast get succeeds). In that case,
|
||||
** 'slot' points to the place to put the value.
|
||||
** Finish a fast set operation (when fast set succeeds).
|
||||
*/
|
||||
#define luaV_finishfastset(L,t,slot,v) \
|
||||
{ setobj2t(L, cast(TValue *,slot), v); \
|
||||
luaC_barrierback(L, gcvalue(t), v); }
|
||||
#define luaV_finishfastset(L,t,v) luaC_barrierback(L, gcvalue(t), v)
|
||||
|
||||
|
||||
/*
|
||||
** Shift right is the same as shift left with a negative 'y'
|
||||
*/
|
||||
#define luaV_shiftr(x,y) luaV_shiftl(x,intop(-, 0, y))
|
||||
|
||||
|
||||
|
||||
LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2);
|
||||
@@ -120,10 +120,10 @@ LUAI_FUNC int luaV_tointeger (const TValue *obj, lua_Integer *p, F2Imod mode);
|
||||
LUAI_FUNC int luaV_tointegerns (const TValue *obj, lua_Integer *p,
|
||||
F2Imod mode);
|
||||
LUAI_FUNC int luaV_flttointeger (lua_Number n, lua_Integer *p, F2Imod mode);
|
||||
LUAI_FUNC void luaV_finishget (lua_State *L, const TValue *t, TValue *key,
|
||||
StkId val, const TValue *slot);
|
||||
LUAI_FUNC lu_byte luaV_finishget (lua_State *L, const TValue *t, TValue *key,
|
||||
StkId val, lu_byte tag);
|
||||
LUAI_FUNC void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
|
||||
TValue *val, const TValue *slot);
|
||||
TValue *val, int aux);
|
||||
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);
|
||||
|
||||
37
lzio.c
37
lzio.c
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lapi.h"
|
||||
#include "llimits.h"
|
||||
#include "lmem.h"
|
||||
#include "lstate.h"
|
||||
@@ -45,17 +46,25 @@ void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) {
|
||||
|
||||
|
||||
/* --------------------------------------------------------------- read --- */
|
||||
|
||||
static int checkbuffer (ZIO *z) {
|
||||
if (z->n == 0) { /* no bytes in buffer? */
|
||||
if (luaZ_fill(z) == EOZ) /* try to read more */
|
||||
return 0; /* no more input */
|
||||
else {
|
||||
z->n++; /* luaZ_fill consumed first byte; put it back */
|
||||
z->p--;
|
||||
}
|
||||
}
|
||||
return 1; /* now buffer has something */
|
||||
}
|
||||
|
||||
|
||||
size_t luaZ_read (ZIO *z, void *b, size_t n) {
|
||||
while (n) {
|
||||
size_t m;
|
||||
if (z->n == 0) { /* no bytes in buffer? */
|
||||
if (luaZ_fill(z) == EOZ) /* try to read more */
|
||||
return n; /* no more input; return number of missing bytes */
|
||||
else {
|
||||
z->n++; /* luaZ_fill consumed first byte; put it back */
|
||||
z->p--;
|
||||
}
|
||||
}
|
||||
if (!checkbuffer(z))
|
||||
return n; /* no more input; return number of missing bytes */
|
||||
m = (n <= z->n) ? n : z->n; /* min. between n and z->n */
|
||||
memcpy(b, z->p, m);
|
||||
z->n -= m;
|
||||
@@ -66,3 +75,15 @@ size_t luaZ_read (ZIO *z, void *b, size_t n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
const void *luaZ_getaddr (ZIO* z, size_t n) {
|
||||
const void *res;
|
||||
if (!checkbuffer(z))
|
||||
return NULL; /* no more input */
|
||||
if (z->n < n) /* not enough bytes? */
|
||||
return NULL; /* block not whole; cannot give an address */
|
||||
res = z->p; /* get block address */
|
||||
z->n -= n; /* consume these bytes */
|
||||
z->p += n;
|
||||
return res;
|
||||
}
|
||||
|
||||
3
lzio.h
3
lzio.h
@@ -32,7 +32,7 @@ typedef struct Mbuffer {
|
||||
#define luaZ_sizebuffer(buff) ((buff)->buffsize)
|
||||
#define luaZ_bufflen(buff) ((buff)->n)
|
||||
|
||||
#define luaZ_buffremove(buff,i) ((buff)->n -= (i))
|
||||
#define luaZ_buffremove(buff,i) ((buff)->n -= cast_sizet(i))
|
||||
#define luaZ_resetbuffer(buff) ((buff)->n = 0)
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader,
|
||||
void *data);
|
||||
LUAI_FUNC size_t luaZ_read (ZIO* z, void *b, size_t n); /* read next n bytes */
|
||||
|
||||
LUAI_FUNC const void *luaZ_getaddr (ZIO* z, size_t n);
|
||||
|
||||
|
||||
/* --------- Private Part ------------------ */
|
||||
|
||||
83
makefile
83
makefile
@@ -8,19 +8,18 @@ CWARNSCPP= \
|
||||
-Wfatal-errors \
|
||||
-Wextra \
|
||||
-Wshadow \
|
||||
-Wsign-compare \
|
||||
-Wundef \
|
||||
-Wwrite-strings \
|
||||
-Wredundant-decls \
|
||||
-Wdisabled-optimization \
|
||||
-Wdouble-promotion \
|
||||
-Wmissing-declarations \
|
||||
-Wconversion \
|
||||
-Wstrict-overflow=2 \
|
||||
# the next warnings might be useful sometimes,
|
||||
# but usually they generate too much noise
|
||||
# -Werror \
|
||||
# -pedantic # warns if we use jump tables \
|
||||
# -Wconversion \
|
||||
# -Wsign-conversion \
|
||||
# -Wstrict-overflow=2 \
|
||||
# -Wformat=2 \
|
||||
# -Wcast-qual \
|
||||
|
||||
@@ -56,18 +55,25 @@ CWARNS= $(CWARNSCPP) $(CWARNSC) $(CWARNGCC)
|
||||
|
||||
# -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 -g
|
||||
|
||||
# The following options help detect "undefined behavior"s that seldom
|
||||
# create problems; some are only available in newer gcc versions. To
|
||||
# use some of them, we also have to define an environment variable
|
||||
# ASAN_OPTIONS="detect_invalid_pointer_pairs=2".
|
||||
# -fsanitize=undefined
|
||||
# -fsanitize=pointer-subtract -fsanitize=address -fsanitize=pointer-compare
|
||||
# TESTS= -DLUA_USER_H='"ltests.h"' -Og -g
|
||||
|
||||
|
||||
LOCAL = $(TESTS) $(CWARNS)
|
||||
|
||||
|
||||
# enable Linux goodies
|
||||
MYCFLAGS= $(LOCAL) -std=c99 -DLUA_USE_LINUX -DLUA_USE_READLINE
|
||||
# To enable Linux goodies, -DLUA_USE_LINUX
|
||||
# For C89, "-std=c89 -DLUA_USE_C89"
|
||||
# Note that Linux/Posix options are not compatible with C89
|
||||
MYCFLAGS= $(LOCAL) -std=c99 -DLUA_USE_LINUX
|
||||
MYLDFLAGS= $(LOCAL) -Wl,-E
|
||||
MYLIBS= -ldl -lreadline
|
||||
MYLIBS= -ldl
|
||||
|
||||
|
||||
CC= gcc
|
||||
@@ -139,40 +145,46 @@ $(ALL_O): makefile ltests.h
|
||||
lapi.o: lapi.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
||||
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lstring.h \
|
||||
ltable.h lundump.h lvm.h
|
||||
lauxlib.o: lauxlib.c lprefix.h lua.h luaconf.h lauxlib.h
|
||||
lbaselib.o: lbaselib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
lauxlib.o: lauxlib.c lprefix.h lua.h luaconf.h lauxlib.h llimits.h
|
||||
lbaselib.o: lbaselib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h \
|
||||
llimits.h
|
||||
lcode.o: lcode.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \
|
||||
llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \
|
||||
ldo.h lgc.h lstring.h ltable.h lvm.h
|
||||
lcorolib.o: lcorolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
ldo.h lgc.h lstring.h ltable.h lvm.h lopnames.h
|
||||
lcorolib.o: lcorolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h \
|
||||
llimits.h
|
||||
lctype.o: lctype.c lprefix.h lctype.h lua.h luaconf.h llimits.h
|
||||
ldblib.o: ldblib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
ldblib.o: ldblib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h llimits.h
|
||||
ldebug.o: ldebug.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
||||
lobject.h ltm.h lzio.h lmem.h lcode.h llex.h lopcodes.h lparser.h \
|
||||
ldebug.h ldo.h lfunc.h lstring.h lgc.h ltable.h lvm.h
|
||||
ldo.o: ldo.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
||||
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lopcodes.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
|
||||
ldump.o: ldump.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
||||
lobject.h ltm.h lzio.h lmem.h lgc.h ltable.h lundump.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
|
||||
liolib.o: liolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h llex.h lstring.h \
|
||||
ltable.h
|
||||
linit.o: linit.c lprefix.h lua.h luaconf.h lualib.h lauxlib.h llimits.h
|
||||
liolib.o: liolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h llimits.h
|
||||
llex.o: llex.c lprefix.h lua.h luaconf.h lctype.h llimits.h ldebug.h \
|
||||
lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lgc.h llex.h lparser.h \
|
||||
lstring.h ltable.h
|
||||
lmathlib.o: lmathlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
lmathlib.o: lmathlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h \
|
||||
llimits.h
|
||||
lmem.o: lmem.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
|
||||
loadlib.o: loadlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
loadlib.o: loadlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h \
|
||||
llimits.h
|
||||
lobject.o: lobject.c lprefix.h lua.h luaconf.h lctype.h llimits.h \
|
||||
ldebug.h lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h \
|
||||
lvm.h
|
||||
lopcodes.o: lopcodes.c lprefix.h lopcodes.h llimits.h lua.h luaconf.h
|
||||
loslib.o: loslib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
lopcodes.o: lopcodes.c lprefix.h lopcodes.h llimits.h lua.h luaconf.h \
|
||||
lobject.h
|
||||
loslib.o: loslib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h llimits.h
|
||||
lparser.o: lparser.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \
|
||||
llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \
|
||||
ldo.h lfunc.h lstring.h lgc.h ltable.h
|
||||
@@ -181,25 +193,28 @@ lstate.o: lstate.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
||||
lstring.h ltable.h
|
||||
lstring.o: lstring.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \
|
||||
lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h
|
||||
lstrlib.o: lstrlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
lstrlib.o: lstrlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h \
|
||||
llimits.h
|
||||
ltable.o: ltable.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
|
||||
ltablib.o: ltablib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
ltablib.o: ltablib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h \
|
||||
llimits.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 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
|
||||
lua.o: lua.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h llimits.h
|
||||
lundump.o: lundump.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 lstring.h lgc.h \
|
||||
lundump.h
|
||||
lutf8lib.o: lutf8lib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||
lvm.o: lvm.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 lopcodes.h lstring.h \
|
||||
ltable.h lvm.h ljumptab.h
|
||||
lzio.o: lzio.c lprefix.h lua.h luaconf.h llimits.h lmem.h lstate.h \
|
||||
lobject.h ltm.h lzio.h
|
||||
ltable.h lundump.h
|
||||
lutf8lib.o: lutf8lib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h \
|
||||
llimits.h
|
||||
lvm.o: lvm.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
||||
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lopcodes.h \
|
||||
lstring.h ltable.h lvm.h ljumptab.h
|
||||
lzio.o: lzio.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
||||
lobject.h ltm.h lzio.h lmem.h
|
||||
|
||||
# (end of Makefile)
|
||||
|
||||
10
manual/2html
10
manual/2html
@@ -8,11 +8,11 @@
|
||||
|
||||
---------------------------------------------------------------
|
||||
header = [[
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Lua 5.4 Reference Manual</title>
|
||||
<title>Lua 5.5 Reference Manual</title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
<link rel="stylesheet" href="lua.css">
|
||||
<link rel="stylesheet" href="manual.css">
|
||||
@@ -23,14 +23,14 @@ header = [[
|
||||
<hr>
|
||||
<h1>
|
||||
<a href="http://www.lua.org/home.html"><img src="logo.gif" alt="[Lua logo]" border="0"></a>
|
||||
Lua 5.4 Reference Manual
|
||||
Lua 5.5 Reference Manual
|
||||
</h1>
|
||||
|
||||
by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes
|
||||
<p>
|
||||
<small>
|
||||
<a href="http://www.lua.org/copyright.html">Copyright</a>
|
||||
© 2021 Lua.org, PUC-Rio. All rights reserved.
|
||||
© 2025 Lua.org, PUC-Rio. All rights reserved.
|
||||
</small>
|
||||
<hr>
|
||||
|
||||
@@ -358,7 +358,7 @@ item = function (s)
|
||||
local t, p = string.match(s, "^([^\n|]+)|()")
|
||||
if t then
|
||||
s = string.sub(s, p)
|
||||
s = Tag.b(t..": ") .. s
|
||||
s = Tag.b(t) ..": " .. s
|
||||
end
|
||||
return Tag.li(fixpara(s))
|
||||
end,
|
||||
|
||||
1543
manual/manual.of
1543
manual/manual.of
File diff suppressed because it is too large
Load Diff
20
onelua.c
20
onelua.c
@@ -1,5 +1,14 @@
|
||||
/*
|
||||
* one.c -- Lua core, libraries, and interpreter in a single file
|
||||
** Lua core, libraries, and interpreter in a single file.
|
||||
** Compiling just this file generates a complete Lua stand-alone
|
||||
** program:
|
||||
**
|
||||
** $ gcc -O2 -std=c99 -o lua onelua.c -lm
|
||||
**
|
||||
** or
|
||||
**
|
||||
** $ gcc -O2 -std=c89 -DLUA_USE_C89 -o lua onelua.c -lm
|
||||
**
|
||||
*/
|
||||
|
||||
/* default is to build the full interpreter */
|
||||
@@ -11,8 +20,12 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* choose suitable platform-specific features */
|
||||
/* some of these may need extra libraries such as -ldl -lreadline -lncurses */
|
||||
|
||||
/*
|
||||
** Choose suitable platform-specific features. Default is no
|
||||
** platform-specific features. Some of these options may need extra
|
||||
** libraries such as -ldl -lreadline -lncurses
|
||||
*/
|
||||
#if 0
|
||||
#define LUA_USE_LINUX
|
||||
#define LUA_USE_MACOSX
|
||||
@@ -20,6 +33,7 @@
|
||||
#define LUA_ANSI
|
||||
#endif
|
||||
|
||||
|
||||
/* no need to change anything below this line ----------------------------- */
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
48
testes/all.lua
Normal file → Executable file
48
testes/all.lua
Normal file → Executable file
@@ -1,9 +1,13 @@
|
||||
#!../lua
|
||||
-- $Id: testes/all.lua $
|
||||
-- See Copyright Notice at the end of this file
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
global <const> *
|
||||
|
||||
local version = "Lua 5.4"
|
||||
global _soft, _port, _nomsg
|
||||
global T
|
||||
|
||||
local version = "Lua 5.5"
|
||||
if _VERSION ~= version then
|
||||
io.stderr:write("This test suite is for ", version,
|
||||
", not for ", _VERSION, "\nExiting tests")
|
||||
@@ -28,14 +32,13 @@ _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
|
||||
_soft = true -- avoid tests that take too long
|
||||
_port = true -- avoid non-portable tests
|
||||
_nomsg = true -- avoid messages about tests not performed
|
||||
end
|
||||
|
||||
-- tests should require debug when needed
|
||||
debug = nil
|
||||
global debug; debug = nil
|
||||
|
||||
|
||||
if usertests then
|
||||
@@ -72,7 +75,7 @@ do -- (
|
||||
|
||||
-- track messages for tests not performed
|
||||
local msgs = {}
|
||||
function Message (m)
|
||||
global function Message (m)
|
||||
if not _nomsg then
|
||||
print(m)
|
||||
msgs[#msgs+1] = string.sub(m, 3, -3)
|
||||
@@ -163,6 +166,7 @@ f()
|
||||
|
||||
dofile('db.lua')
|
||||
assert(dofile('calls.lua') == deep and deep)
|
||||
_G.deep = nil
|
||||
olddofile('strings.lua')
|
||||
olddofile('literals.lua')
|
||||
dofile('tpack.lua')
|
||||
@@ -182,6 +186,7 @@ dofile('nextvar.lua')
|
||||
dofile('pm.lua')
|
||||
dofile('utf8.lua')
|
||||
dofile('api.lua')
|
||||
dofile('memerr.lua')
|
||||
assert(dofile('events.lua') == 12)
|
||||
dofile('vararg.lua')
|
||||
dofile('closure.lua')
|
||||
@@ -282,30 +287,3 @@ 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.
|
||||
*****************************************************************************
|
||||
]]
|
||||
|
||||
|
||||
487
testes/api.lua
487
testes/api.lua
@@ -1,5 +1,5 @@
|
||||
-- $Id: testes/api.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
if T==nil then
|
||||
(Message or print)('\n >>> testC not active: skipping API tests <<<\n')
|
||||
@@ -11,10 +11,7 @@ local debug = require "debug"
|
||||
local pack = table.pack
|
||||
|
||||
|
||||
-- standard error message for memory errors
|
||||
local MEMERRMSG = "not enough memory"
|
||||
|
||||
function tcheck (t1, t2)
|
||||
local function tcheck (t1, t2)
|
||||
assert(t1.n == (t2.n or #t2) + 1)
|
||||
for i = 2, t1.n do assert(t1[i] == t2[i - 1]) end
|
||||
end
|
||||
@@ -28,7 +25,7 @@ end
|
||||
|
||||
print('testing C API')
|
||||
|
||||
a = T.testC("pushvalue R; return 1")
|
||||
local a = T.testC("pushvalue R; return 1")
|
||||
assert(a == debug.getregistry())
|
||||
|
||||
|
||||
@@ -43,10 +40,10 @@ a = T.d2s(12458954321123.0)
|
||||
assert(a == string.pack("d", 12458954321123.0))
|
||||
assert(T.s2d(a) == 12458954321123.0)
|
||||
|
||||
a,b,c = T.testC("pushnum 1; pushnum 2; pushnum 3; return 2")
|
||||
local a,b,c = T.testC("pushnum 1; pushnum 2; pushnum 3; return 2")
|
||||
assert(a == 2 and b == 3 and not c)
|
||||
|
||||
f = T.makeCfunc("pushnum 1; pushnum 2; pushnum 3; return 2")
|
||||
local f = T.makeCfunc("pushnum 1; pushnum 2; pushnum 3; return 2")
|
||||
a,b,c = f()
|
||||
assert(a == 2 and b == 3 and not c)
|
||||
|
||||
@@ -61,7 +58,7 @@ assert(a==false and b==true and c==false)
|
||||
a,b,c = T.testC("gettop; return 2", 10, 20, 30, 40)
|
||||
assert(a == 40 and b == 5 and not c)
|
||||
|
||||
t = pack(T.testC("settop 5; return *", 2, 3))
|
||||
local t = pack(T.testC("settop 5; return *", 2, 3))
|
||||
tcheck(t, {n=4,2,3})
|
||||
|
||||
t = pack(T.testC("settop 0; settop 15; return 10", 3, 1, 23))
|
||||
@@ -117,7 +114,7 @@ end
|
||||
|
||||
-- testing warnings
|
||||
T.testC([[
|
||||
warningC "#This shold be a"
|
||||
warningC "#This should be a"
|
||||
warningC " single "
|
||||
warning "warning"
|
||||
warningC "#This should be "
|
||||
@@ -165,17 +162,35 @@ do -- test returning more results than fit in the caller stack
|
||||
end
|
||||
|
||||
|
||||
do -- testing multiple returns
|
||||
local function foo (n)
|
||||
if n > 0 then return n, foo(n - 1) end
|
||||
end
|
||||
|
||||
local t = {T.testC("call 1 10; return 10", foo, 20)}
|
||||
assert(t[1] == 20 and t[10] == 11 and t[11] == nil)
|
||||
|
||||
local t = table.pack(T.testC("call 1 10; return 10", foo, 2))
|
||||
assert(t[1] == 2 and t[2] == 1 and t[3] == nil and t.n == 10)
|
||||
|
||||
local t = {T.testC([[
|
||||
checkstack 300 "error"; call 1 250; return 250]], foo, 250)}
|
||||
assert(t[1] == 250 and t[250] == 1 and t[251] == nil)
|
||||
end
|
||||
|
||||
|
||||
-- testing globals
|
||||
_G.a = 14; _G.b = "a31"
|
||||
_G.AA = 14; _G.BB = "a31"
|
||||
local a = {T.testC[[
|
||||
getglobal a;
|
||||
getglobal b;
|
||||
getglobal b;
|
||||
setglobal a;
|
||||
getglobal AA;
|
||||
getglobal BB;
|
||||
getglobal BB;
|
||||
setglobal AA;
|
||||
return *
|
||||
]]}
|
||||
assert(a[2] == 14 and a[3] == "a31" and a[4] == nil and _G.a == "a31")
|
||||
assert(a[2] == 14 and a[3] == "a31" and a[4] == nil and _G.AA == "a31")
|
||||
|
||||
_G.AA, _G.BB = nil
|
||||
|
||||
-- testing arith
|
||||
assert(T.testC("pushnum 10; pushnum 20; arith /; return 1") == 0.5)
|
||||
@@ -199,13 +214,14 @@ a,b,c = T.testC([[pushnum 1;
|
||||
pushstring 10; arith _;
|
||||
pushstring 5; return 3]])
|
||||
assert(a == 1 and b == -10 and c == "5")
|
||||
mt = {__add = function (a,b) return setmetatable({a[1] + b[1]}, mt) end,
|
||||
local mt = {
|
||||
__add = function (a,b) return setmetatable({a[1] + b[1]}, mt) end,
|
||||
__mod = function (a,b) return setmetatable({a[1] % b[1]}, mt) end,
|
||||
__unm = function (a) return setmetatable({a[1]* 2}, mt) end}
|
||||
a,b,c = setmetatable({4}, mt),
|
||||
setmetatable({8}, mt),
|
||||
setmetatable({-3}, mt)
|
||||
x,y,z = T.testC("arith +; return 2", 10, a, b)
|
||||
local x,y,z = T.testC("arith +; return 2", 10, a, b)
|
||||
assert(x == 10 and y[1] == 12 and z == nil)
|
||||
assert(T.testC("arith %; return 1", a, c)[1] == 4%-3)
|
||||
assert(T.testC("arith _; arith +; arith %; return 1", b, a, c)[1] ==
|
||||
@@ -312,9 +328,9 @@ assert(T.testC("concat 1; return 1", "xuxu") == "xuxu")
|
||||
|
||||
-- testing lua_is
|
||||
|
||||
function B(x) return x and 1 or 0 end
|
||||
local function B (x) return x and 1 or 0 end
|
||||
|
||||
function count (x, n)
|
||||
local function count (x, n)
|
||||
n = n or 2
|
||||
local prog = [[
|
||||
isnumber %d;
|
||||
@@ -345,7 +361,7 @@ assert(count(nil, 15) == 100)
|
||||
|
||||
-- testing lua_to...
|
||||
|
||||
function to (s, x, n)
|
||||
local function to (s, x, n)
|
||||
n = n or 2
|
||||
return T.testC(string.format("%s %d; return 1", s, n), x)
|
||||
end
|
||||
@@ -397,6 +413,10 @@ do
|
||||
-- trivial error
|
||||
assert(T.checkpanic("pushstring hi; error") == "hi")
|
||||
|
||||
-- thread status inside panic (bug in 5.4.4)
|
||||
assert(T.checkpanic("pushstring hi; error", "threadstatus; return 2") ==
|
||||
"ERRRUN")
|
||||
|
||||
-- using the stack inside panic
|
||||
assert(T.checkpanic("pushstring hi; error;",
|
||||
[[checkstack 5 XX
|
||||
@@ -405,20 +425,30 @@ do
|
||||
concat 3]]) == "hi alo mundo")
|
||||
|
||||
-- "argerror" without frames
|
||||
assert(T.checkpanic("loadstring 4") ==
|
||||
assert(T.checkpanic("loadstring 4 name bt") ==
|
||||
"bad argument #4 (string expected, got no value)")
|
||||
|
||||
|
||||
-- memory error
|
||||
T.totalmem(T.totalmem()+10000) -- set low memory limit (+10k)
|
||||
assert(T.checkpanic("newuserdata 20000") == MEMERRMSG)
|
||||
T.totalmem(0) -- restore high limit
|
||||
-- memory error + thread status
|
||||
local x = T.checkpanic(
|
||||
[[ alloccount 0 # force a memory error in next line
|
||||
newtable
|
||||
]],
|
||||
[[
|
||||
alloccount -1 # allow free allocations again
|
||||
pushstring XX
|
||||
threadstatus
|
||||
concat 2 # to make sure message came from here
|
||||
return 1
|
||||
]])
|
||||
T.alloccount()
|
||||
assert(x == "XX" .. "not enough memory")
|
||||
|
||||
-- stack error
|
||||
if not _soft then
|
||||
local msg = T.checkpanic[[
|
||||
pushstring "function f() f() end"
|
||||
loadstring -1; call 0 0
|
||||
loadstring -1 name t; call 0 0
|
||||
getglobal f; call 0 0
|
||||
]]
|
||||
assert(string.find(msg, "stack overflow"))
|
||||
@@ -428,7 +458,7 @@ do
|
||||
assert(T.checkpanic([[
|
||||
pushstring "return {__close = function () Y = 'ho'; end}"
|
||||
newtable
|
||||
loadstring -2
|
||||
loadstring -2 name t
|
||||
call 0 1
|
||||
setmetatable -2
|
||||
toclose -1
|
||||
@@ -456,6 +486,8 @@ if not _soft then
|
||||
print'+'
|
||||
end
|
||||
|
||||
|
||||
|
||||
local lim = _soft and 500 or 12000
|
||||
local prog = {"checkstack " .. (lim * 2 + 100) .. "msg", "newtable"}
|
||||
for i = 1,lim do
|
||||
@@ -463,7 +495,7 @@ for i = 1,lim do
|
||||
prog[#prog + 1] = "pushnum " .. i * 10
|
||||
end
|
||||
|
||||
prog[#prog + 1] = "rawgeti R 2" -- get global table in registry
|
||||
prog[#prog + 1] = "rawgeti R !G" -- get global table in registry
|
||||
prog[#prog + 1] = "insert " .. -(2*lim + 2)
|
||||
|
||||
for i = 1,lim do
|
||||
@@ -479,39 +511,98 @@ for i = 1,lim do assert(t[i] == i*10); t[i] = undef end
|
||||
assert(next(t) == nil)
|
||||
prog, g, t = nil
|
||||
|
||||
do -- shrink stack
|
||||
local m1, m2 = 0, collectgarbage"count" * 1024
|
||||
while m1 ~= m2 do -- repeat until stable
|
||||
collectgarbage()
|
||||
m1 = m2
|
||||
m2 = collectgarbage"count" * 1024
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- testing errors
|
||||
|
||||
a = T.testC([[
|
||||
loadstring 2; pcall 0 1 0;
|
||||
loadstring 2 name t; pcall 0 1 0;
|
||||
pushvalue 3; insert -2; pcall 1 1 0;
|
||||
pcall 0 0 0;
|
||||
return 1
|
||||
]], "x=150", function (a) assert(a==nil); return 3 end)
|
||||
]], "XX=150", function (a) assert(a==nil); return 3 end)
|
||||
|
||||
assert(type(a) == 'string' and x == 150)
|
||||
assert(type(a) == 'string' and XX == 150)
|
||||
_G.XX = nil
|
||||
|
||||
function check3(p, ...)
|
||||
local function check3(p, ...)
|
||||
local arg = {...}
|
||||
assert(#arg == 3)
|
||||
assert(string.find(arg[3], p))
|
||||
end
|
||||
check3(":1:", T.testC("loadstring 2; return *", "x="))
|
||||
check3(":1:", T.testC("loadstring 2 name t; return *", "x="))
|
||||
check3("%.", T.testC("loadfile 2; return *", "."))
|
||||
check3("xxxx", T.testC("loadfile 2; return *", "xxxx"))
|
||||
|
||||
-- test errors in non protected threads
|
||||
function checkerrnopro (code, msg)
|
||||
local function checkerrnopro (code, msg)
|
||||
local th = coroutine.create(function () end) -- create new thread
|
||||
local stt, err = pcall(T.testC, th, code) -- run code there
|
||||
assert(not stt and string.find(err, msg))
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
print("testing load of binaries in fixed buffers")
|
||||
local source = {}
|
||||
local N = 1000
|
||||
-- create a somewhat "large" source
|
||||
for i = 1, N do source[i] = "X = X + 1; " end
|
||||
-- add a long string to the source
|
||||
source[#source + 1] = string.format("Y = '%s'", string.rep("a", N));
|
||||
source = table.concat(source)
|
||||
-- give chunk an explicit name to avoid using source as name
|
||||
source = load(source, "name1")
|
||||
-- dump without debug information
|
||||
source = string.dump(source, true)
|
||||
-- each "X=X+1" generates 4 opcodes with 4 bytes each, plus the string
|
||||
assert(#source > N * 4 * 4 + N)
|
||||
collectgarbage(); collectgarbage()
|
||||
local m1 = collectgarbage"count" * 1024
|
||||
-- load dump using fixed buffer
|
||||
local code = T.testC([[
|
||||
loadstring 2 name B;
|
||||
return 1
|
||||
]], source)
|
||||
collectgarbage()
|
||||
local m2 = collectgarbage"count" * 1024
|
||||
-- load used fewer than 400 bytes. Code alone has more than 3*N bytes,
|
||||
-- and string literal has N bytes. Both were not loaded.
|
||||
assert(m2 > m1 and m2 - m1 < 400)
|
||||
X = 0; code(); assert(X == N and Y == string.rep("a", N))
|
||||
X = nil; Y = nil
|
||||
|
||||
-- testing debug info in fixed buffers
|
||||
source = {"X = 0"}
|
||||
for i = 2, 300 do source[i] = "X = X + 1" end
|
||||
source[#source + 1] = "X = X + {}" -- error in last line
|
||||
source = table.concat(source, "\n")
|
||||
source = load(source, "name1")
|
||||
source = string.dump(source)
|
||||
-- load dump using fixed buffer
|
||||
local code = T.testC([[
|
||||
loadstring 2 name B;
|
||||
return 1
|
||||
]], source)
|
||||
checkerr(":301:", code) -- correct line information
|
||||
end
|
||||
|
||||
|
||||
if not _soft then
|
||||
collectgarbage("stop") -- avoid __gc with full stack
|
||||
checkerrnopro("pushnum 3; call 0 0", "attempt to call")
|
||||
print"testing stack overflow in unprotected thread"
|
||||
function f () f() end
|
||||
checkerrnopro("getglobal 'f'; call 0 0;", "stack overflow")
|
||||
function F () F() end
|
||||
checkerrnopro("getglobal 'F'; call 0 0;", "stack overflow")
|
||||
F = nil
|
||||
collectgarbage("restart")
|
||||
end
|
||||
print"+"
|
||||
@@ -588,7 +679,7 @@ assert(a[a] == "x")
|
||||
|
||||
b = setmetatable({p = a}, {})
|
||||
getmetatable(b).__index = function (t, i) return t.p[i] end
|
||||
k, x = T.testC("gettable 3, return 2", 4, b, 20, 35, "x")
|
||||
local k, x = T.testC("gettable 3, return 2", 4, b, 20, 35, "x")
|
||||
assert(x == 15 and k == 35)
|
||||
k = T.testC("getfield 2 y, return 1", b)
|
||||
assert(k == 12)
|
||||
@@ -719,7 +810,7 @@ assert(debug.getuservalue(b) == 134)
|
||||
-- test barrier for uservalues
|
||||
do
|
||||
local oldmode = collectgarbage("incremental")
|
||||
T.gcstate("atomic")
|
||||
T.gcstate("enteratomic")
|
||||
assert(T.gccolor(b) == "black")
|
||||
debug.setuservalue(b, {x = 100})
|
||||
T.gcstate("pause") -- complete collection
|
||||
@@ -748,8 +839,8 @@ local i = T.ref{}
|
||||
T.unref(i)
|
||||
assert(T.ref{} == i)
|
||||
|
||||
Arr = {}
|
||||
Lim = 100
|
||||
local Arr = {}
|
||||
local Lim = 100
|
||||
for i=1,Lim do -- lock many objects
|
||||
Arr[i] = T.ref({})
|
||||
end
|
||||
@@ -761,7 +852,7 @@ for i=1,Lim do -- unlock all them
|
||||
T.unref(Arr[i])
|
||||
end
|
||||
|
||||
function printlocks ()
|
||||
local function printlocks ()
|
||||
local f = T.makeCfunc("gettable R; return 1")
|
||||
local n = f("n")
|
||||
print("n", n)
|
||||
@@ -793,8 +884,8 @@ assert(type(T.getref(a)) == 'table')
|
||||
|
||||
|
||||
-- colect in cl the `val' of all collected userdata
|
||||
tt = {}
|
||||
cl = {n=0}
|
||||
local tt = {}
|
||||
local cl = {n=0}
|
||||
A = nil; B = nil
|
||||
local F
|
||||
F = function (x)
|
||||
@@ -804,20 +895,20 @@ F = function (x)
|
||||
d = nil
|
||||
assert(debug.getmetatable(x).__gc == F)
|
||||
assert(load("table.insert({}, {})"))() -- create more garbage
|
||||
collectgarbage() -- force a GC during GC
|
||||
assert(debug.getmetatable(x).__gc == F) -- previous GC did not mess this?
|
||||
assert(not collectgarbage()) -- GC during GC (no op)
|
||||
local dummy = {} -- create more garbage during GC
|
||||
if A ~= nil then
|
||||
assert(type(A) == "userdata")
|
||||
assert(T.udataval(A) == B)
|
||||
debug.getmetatable(A) -- just access it
|
||||
end
|
||||
A = x -- ressucita userdata
|
||||
A = x -- resurrect userdata
|
||||
B = udval
|
||||
return 1,2,3
|
||||
end
|
||||
tt.__gc = F
|
||||
|
||||
|
||||
-- test whether udate collection frees memory in the right time
|
||||
do
|
||||
collectgarbage();
|
||||
@@ -854,9 +945,9 @@ end
|
||||
collectgarbage("stop")
|
||||
|
||||
-- create 3 userdatas with tag `tt'
|
||||
a = T.newuserdata(0); debug.setmetatable(a, tt); na = T.udataval(a)
|
||||
b = T.newuserdata(0); debug.setmetatable(b, tt); nb = T.udataval(b)
|
||||
c = T.newuserdata(0); debug.setmetatable(c, tt); nc = T.udataval(c)
|
||||
a = T.newuserdata(0); debug.setmetatable(a, tt); local na = T.udataval(a)
|
||||
b = T.newuserdata(0); debug.setmetatable(b, tt); local nb = T.udataval(b)
|
||||
c = T.newuserdata(0); debug.setmetatable(c, tt); local nc = T.udataval(c)
|
||||
|
||||
-- create userdata without meta table
|
||||
x = T.newuserdata(4)
|
||||
@@ -867,29 +958,31 @@ checkerr("FILE%* expected, got userdata", io.input, x)
|
||||
|
||||
assert(debug.getmetatable(x) == nil and debug.getmetatable(y) == nil)
|
||||
|
||||
d=T.ref(a);
|
||||
e=T.ref(b);
|
||||
f=T.ref(c);
|
||||
t = {T.getref(d), T.getref(e), T.getref(f)}
|
||||
-- Test references in an arbitrary table
|
||||
local reftable = {}
|
||||
local d = T.ref(a, reftable);
|
||||
local e = T.ref(b, reftable);
|
||||
local f = T.ref(c, reftable);
|
||||
t = {T.getref(d, reftable), T.getref(e, reftable), T.getref(f, reftable)}
|
||||
assert(t[1] == a and t[2] == b and t[3] == c)
|
||||
|
||||
t=nil; a=nil; c=nil;
|
||||
T.unref(e); T.unref(f)
|
||||
T.unref(e, reftable); T.unref(f, reftable)
|
||||
|
||||
collectgarbage()
|
||||
|
||||
-- check that unref objects have been collected
|
||||
assert(#cl == 1 and cl[1] == nc)
|
||||
|
||||
x = T.getref(d)
|
||||
x = T.getref(d, reftable)
|
||||
assert(type(x) == 'userdata' and debug.getmetatable(x) == tt)
|
||||
x =nil
|
||||
tt.b = b -- create cycle
|
||||
tt=nil -- frees tt for GC
|
||||
A = nil
|
||||
b = nil
|
||||
T.unref(d);
|
||||
n5 = T.newuserdata(0)
|
||||
T.unref(d, reftable);
|
||||
local n5 = T.newuserdata(0)
|
||||
debug.setmetatable(n5, {__gc=F})
|
||||
n5 = T.udataval(n5)
|
||||
collectgarbage()
|
||||
@@ -897,6 +990,21 @@ assert(#cl == 4)
|
||||
-- check order of collection
|
||||
assert(cl[2] == n5 and cl[3] == nb and cl[4] == na)
|
||||
|
||||
-- reuse a reference in 'reftable'
|
||||
T.unref(T.ref(23, reftable), reftable)
|
||||
|
||||
do -- check reftable
|
||||
local count = 0
|
||||
local i = 1
|
||||
while reftable[i] ~= 0 do
|
||||
i = reftable[i] -- traverse linked list of free references
|
||||
count = count + 1
|
||||
end
|
||||
-- maximum number of simultaneously locked objects was 3
|
||||
assert(count == 3 and #reftable == 3 + 1) -- +1 for reserved [1]
|
||||
end
|
||||
|
||||
|
||||
collectgarbage"restart"
|
||||
|
||||
|
||||
@@ -960,11 +1068,11 @@ print'+'
|
||||
|
||||
|
||||
-- testing changing hooks during hooks
|
||||
_G.t = {}
|
||||
_G.TT = {}
|
||||
T.sethook([[
|
||||
# set a line hook after 3 count hooks
|
||||
sethook 4 0 '
|
||||
getglobal t;
|
||||
getglobal TT;
|
||||
pushvalue -3; append -2
|
||||
pushvalue -2; append -2
|
||||
']], "c", 3)
|
||||
@@ -974,12 +1082,13 @@ a = 1 -- count hook (set line hook)
|
||||
a = 1 -- line hook
|
||||
a = 1 -- line hook
|
||||
debug.sethook()
|
||||
t = _G.t
|
||||
local t = _G.TT
|
||||
assert(t[1] == "line")
|
||||
line = t[2]
|
||||
local line = t[2]
|
||||
assert(t[3] == "line" and t[4] == line + 1)
|
||||
assert(t[5] == "line" and t[6] == line + 2)
|
||||
assert(t[7] == nil)
|
||||
_G.TT = nil
|
||||
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
@@ -1004,6 +1113,7 @@ do -- testing errors during GC
|
||||
collectgarbage("restart")
|
||||
warn("@on")
|
||||
end
|
||||
_G.A = nil
|
||||
-------------------------------------------------------------------------
|
||||
-- test for userdata vals
|
||||
do
|
||||
@@ -1033,17 +1143,19 @@ assert(a == 'alo' and b == '3')
|
||||
|
||||
T.doremote(L1, "_ERRORMESSAGE = nil")
|
||||
-- error: `sin' is not defined
|
||||
a, _, b = T.doremote(L1, "return sin(1)")
|
||||
assert(a == nil and b == 2) -- 2 == run-time error
|
||||
a, b, c = T.doremote(L1, "return sin(1)")
|
||||
assert(a == nil and c == 2) -- 2 == run-time error
|
||||
|
||||
-- error: syntax error
|
||||
a, b, c = T.doremote(L1, "return a+")
|
||||
assert(a == nil and c == 3 and type(b) == "string") -- 3 == syntax error
|
||||
|
||||
T.loadlib(L1)
|
||||
T.loadlib(L1, 2, ~2) -- load only 'package', preload all others
|
||||
a, b, c = T.doremote(L1, [[
|
||||
string = require'string'
|
||||
a = require'_G'; assert(a == _G and require("_G") == a)
|
||||
local initialG = _G -- not loaded yet
|
||||
local a = require'_G'; assert(a == _G and require("_G") == a)
|
||||
assert(initialG == nil and io == nil) -- now we have 'assert'
|
||||
io = require'io'; assert(type(io.read) == "function")
|
||||
assert(require("io") == io)
|
||||
a = require'table'; assert(type(a.insert) == "function")
|
||||
@@ -1057,7 +1169,7 @@ T.closestate(L1);
|
||||
|
||||
|
||||
L1 = T.newstate()
|
||||
T.loadlib(L1)
|
||||
T.loadlib(L1, 0, 0)
|
||||
T.doremote(L1, "a = {}")
|
||||
T.testC(L1, [[getglobal "a"; pushstring "x"; pushint 1;
|
||||
settable -3]])
|
||||
@@ -1109,7 +1221,8 @@ do
|
||||
local a, b = pcall(T.makeCfunc[[
|
||||
call 0 1 # create resource
|
||||
toclose -1 # mark it to be closed
|
||||
error # resource is the error object
|
||||
pushvalue -1 # replicate it as error object
|
||||
error # resource right after error object
|
||||
]], newresource)
|
||||
assert(a == false and b[1] == 11)
|
||||
assert(#openresource == 0) -- was closed
|
||||
@@ -1185,238 +1298,6 @@ do
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
** {==================================================================
|
||||
** Testing memory limits
|
||||
** ===================================================================
|
||||
--]]
|
||||
|
||||
print("memory-allocation errors")
|
||||
|
||||
checkerr("block too big", T.newuserdata, math.maxinteger)
|
||||
collectgarbage()
|
||||
local f = load"local a={}; for i=1,100000 do a[i]=i end"
|
||||
T.alloccount(10)
|
||||
checkerr(MEMERRMSG, f)
|
||||
T.alloccount() -- remove limit
|
||||
|
||||
|
||||
-- test memory errors; increase limit for maximum memory by steps,
|
||||
-- o that we get memory errors in all allocations of a given
|
||||
-- task, until there is enough memory to complete the task without
|
||||
-- errors.
|
||||
function testbytes (s, f)
|
||||
collectgarbage()
|
||||
local M = T.totalmem()
|
||||
local oldM = M
|
||||
local a,b = nil
|
||||
while true do
|
||||
collectgarbage(); collectgarbage()
|
||||
T.totalmem(M)
|
||||
a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
|
||||
T.totalmem(0) -- remove limit
|
||||
if a and b == "OK" then break end -- stop when no more errors
|
||||
if b ~= "OK" and b ~= MEMERRMSG then -- not a memory error?
|
||||
error(a, 0) -- propagate it
|
||||
end
|
||||
M = M + 7 -- increase memory limit
|
||||
end
|
||||
print(string.format("minimum memory for %s: %d bytes", s, M - oldM))
|
||||
return a
|
||||
end
|
||||
|
||||
-- test memory errors; increase limit for number of allocations one
|
||||
-- by one, so that we get memory errors in all allocations of a given
|
||||
-- task, until there is enough allocations to complete the task without
|
||||
-- errors.
|
||||
|
||||
function testalloc (s, f)
|
||||
collectgarbage()
|
||||
local M = 0
|
||||
local a,b = nil
|
||||
while true do
|
||||
collectgarbage(); collectgarbage()
|
||||
T.alloccount(M)
|
||||
a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
|
||||
T.alloccount() -- remove limit
|
||||
if a and b == "OK" then break end -- stop when no more errors
|
||||
if b ~= "OK" and b ~= MEMERRMSG then -- not a memory error?
|
||||
error(a, 0) -- propagate it
|
||||
end
|
||||
M = M + 1 -- increase allocation limit
|
||||
end
|
||||
print(string.format("minimum allocations for %s: %d allocations", s, M))
|
||||
return a
|
||||
end
|
||||
|
||||
|
||||
local function testamem (s, f)
|
||||
testalloc(s, f)
|
||||
return testbytes(s, f)
|
||||
end
|
||||
|
||||
|
||||
-- doing nothing
|
||||
b = testamem("doing nothing", function () return 10 end)
|
||||
assert(b == 10)
|
||||
|
||||
-- testing memory errors when creating a new state
|
||||
|
||||
testamem("state creation", function ()
|
||||
local st = T.newstate()
|
||||
if st then T.closestate(st) end -- close new state
|
||||
return st
|
||||
end)
|
||||
|
||||
testamem("empty-table creation", function ()
|
||||
return {}
|
||||
end)
|
||||
|
||||
testamem("string creation", function ()
|
||||
return "XXX" .. "YYY"
|
||||
end)
|
||||
|
||||
testamem("coroutine creation", function()
|
||||
return coroutine.create(print)
|
||||
end)
|
||||
|
||||
|
||||
-- testing to-be-closed variables
|
||||
testamem("to-be-closed variables", function()
|
||||
local flag
|
||||
do
|
||||
local x <close> =
|
||||
setmetatable({}, {__close = function () flag = true end})
|
||||
flag = false
|
||||
local x = {}
|
||||
end
|
||||
return flag
|
||||
end)
|
||||
|
||||
|
||||
-- testing threads
|
||||
|
||||
-- get main thread from registry (at index LUA_RIDX_MAINTHREAD == 1)
|
||||
mt = T.testC("rawgeti R 1; return 1")
|
||||
assert(type(mt) == "thread" and coroutine.running() == mt)
|
||||
|
||||
|
||||
|
||||
function expand (n,s)
|
||||
if n==0 then return "" end
|
||||
local e = string.rep("=", n)
|
||||
return string.format("T.doonnewstack([%s[ %s;\n collectgarbage(); %s]%s])\n",
|
||||
e, s, expand(n-1,s), e)
|
||||
end
|
||||
|
||||
G=0; collectgarbage(); a =collectgarbage("count")
|
||||
load(expand(20,"G=G+1"))()
|
||||
assert(G==20); collectgarbage(); -- assert(gcinfo() <= a+1)
|
||||
|
||||
testamem("running code on new thread", function ()
|
||||
return T.doonnewstack("x=1") == 0 -- try to create thread
|
||||
end)
|
||||
|
||||
|
||||
-- testing memory x compiler
|
||||
|
||||
testamem("loadstring", function ()
|
||||
return load("x=1") -- try to do load a string
|
||||
end)
|
||||
|
||||
|
||||
local testprog = [[
|
||||
local function foo () return end
|
||||
local t = {"x"}
|
||||
a = "aaa"
|
||||
for i = 1, #t do a=a..t[i] end
|
||||
return true
|
||||
]]
|
||||
|
||||
-- testing memory x dofile
|
||||
_G.a = nil
|
||||
local t =os.tmpname()
|
||||
local f = assert(io.open(t, "w"))
|
||||
f:write(testprog)
|
||||
f:close()
|
||||
testamem("dofile", function ()
|
||||
local a = loadfile(t)
|
||||
return a and a()
|
||||
end)
|
||||
assert(os.remove(t))
|
||||
assert(_G.a == "aaax")
|
||||
|
||||
|
||||
-- other generic tests
|
||||
|
||||
testamem("gsub", function ()
|
||||
local a, b = string.gsub("alo alo", "(a)", function (x) return x..'b' end)
|
||||
return (a == 'ablo ablo')
|
||||
end)
|
||||
|
||||
testamem("dump/undump", function ()
|
||||
local a = load(testprog)
|
||||
local b = a and string.dump(a)
|
||||
a = b and load(b)
|
||||
return a and a()
|
||||
end)
|
||||
|
||||
local t = os.tmpname()
|
||||
testamem("file creation", function ()
|
||||
local f = assert(io.open(t, 'w'))
|
||||
assert (not io.open"nomenaoexistente")
|
||||
io.close(f);
|
||||
return not loadfile'nomenaoexistente'
|
||||
end)
|
||||
assert(os.remove(t))
|
||||
|
||||
testamem("table creation", function ()
|
||||
local a, lim = {}, 10
|
||||
for i=1,lim do a[i] = i; a[i..'a'] = {} end
|
||||
return (type(a[lim..'a']) == 'table' and a[lim] == lim)
|
||||
end)
|
||||
|
||||
testamem("constructors", function ()
|
||||
local a = {10, 20, 30, 40, 50; a=1, b=2, c=3, d=4, e=5}
|
||||
return (type(a) == 'table' and a.e == 5)
|
||||
end)
|
||||
|
||||
local a = 1
|
||||
close = nil
|
||||
testamem("closure creation", function ()
|
||||
function close (b)
|
||||
return function (x) return b + x end
|
||||
end
|
||||
return (close(2)(4) == 6)
|
||||
end)
|
||||
|
||||
testamem("using coroutines", function ()
|
||||
local a = coroutine.wrap(function ()
|
||||
coroutine.yield(string.rep("a", 10))
|
||||
return {}
|
||||
end)
|
||||
assert(string.len(a()) == 10)
|
||||
return a()
|
||||
end)
|
||||
|
||||
do -- auxiliary buffer
|
||||
local lim = 100
|
||||
local a = {}; for i = 1, lim do a[i] = "01234567890123456789" end
|
||||
testamem("auxiliary buffer", function ()
|
||||
return (#table.concat(a, ",") == 20*lim + lim - 1)
|
||||
end)
|
||||
end
|
||||
|
||||
testamem("growing stack", function ()
|
||||
local function foo (n)
|
||||
if n == 0 then return 1 else return 1 + foo(n - 1) end
|
||||
end
|
||||
return foo(100)
|
||||
end)
|
||||
|
||||
-- }==================================================================
|
||||
|
||||
|
||||
do -- testing failing in 'lua_checkstack'
|
||||
local res = T.testC([[rawcheckstack 500000; return 1]])
|
||||
assert(res == false)
|
||||
@@ -1437,10 +1318,10 @@ end
|
||||
|
||||
do -- garbage collection with no extra memory
|
||||
local L = T.newstate()
|
||||
T.loadlib(L)
|
||||
T.loadlib(L, 1 | 2, 0) -- load _G and 'package'
|
||||
local res = (T.doremote(L, [[
|
||||
_ENV = require"_G"
|
||||
local T = require"T"
|
||||
_ENV = _G
|
||||
assert(string == nil)
|
||||
local a = {}
|
||||
for i = 1, 1000 do a[i] = 'i' .. i end -- grow string table
|
||||
local stsize, stuse = T.querystr()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- $Id: testes/attrib.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
print "testing require"
|
||||
|
||||
@@ -85,7 +85,7 @@ local DIR = "libs" .. dirsep
|
||||
|
||||
-- prepend DIR to a name and correct directory separators
|
||||
local function D (x)
|
||||
x = string.gsub(x, "/", dirsep)
|
||||
local x = string.gsub(x, "/", dirsep)
|
||||
return DIR .. x
|
||||
end
|
||||
|
||||
@@ -106,7 +106,7 @@ local function createfiles (files, preextras, posextras)
|
||||
end
|
||||
end
|
||||
|
||||
function removefiles (files)
|
||||
local function removefiles (files)
|
||||
for n in pairs(files) do
|
||||
os.remove(D(n))
|
||||
end
|
||||
@@ -154,10 +154,9 @@ local try = function (p, n, r, ext)
|
||||
assert(ext == x)
|
||||
end
|
||||
|
||||
a = require"names"
|
||||
local 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")
|
||||
@@ -191,6 +190,7 @@ try("X", "XXxX", AA, "libs/XXxX")
|
||||
|
||||
|
||||
removefiles(files)
|
||||
NAME, REQUIRED, AA, B = nil
|
||||
|
||||
|
||||
-- testing require of sub-packages
|
||||
@@ -223,7 +223,7 @@ assert(require"P1" == m and m.AA == 10)
|
||||
|
||||
|
||||
removefiles(files)
|
||||
|
||||
AA = nil
|
||||
|
||||
package.path = ""
|
||||
assert(not pcall(require, "file_does_not_exist"))
|
||||
@@ -236,7 +236,7 @@ package.path = oldpath
|
||||
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)
|
||||
local t = string.gsub(t, "?", fname)
|
||||
assert(string.find(err, t, 1, true))
|
||||
end
|
||||
|
||||
@@ -305,6 +305,7 @@ else
|
||||
assert(_ENV.x == "lib1.sub" and _ENV.y == DC"lib1")
|
||||
assert(string.find(ext, "libs/lib1", 1, true))
|
||||
assert(fs.id(45) == 45)
|
||||
_ENV.x, _ENV.y = nil
|
||||
end
|
||||
|
||||
_ENV = _G
|
||||
@@ -338,10 +339,10 @@ print("testing assignments, logical operators, and constructors")
|
||||
|
||||
local res, res2 = 27
|
||||
|
||||
a, b = 1, 2+3
|
||||
local a, b = 1, 2+3
|
||||
assert(a==1 and b==5)
|
||||
a={}
|
||||
function f() return 10, 11, 12 end
|
||||
local 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'
|
||||
@@ -353,15 +354,15 @@ do
|
||||
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 = 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
|
||||
local 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
|
||||
@@ -419,6 +420,7 @@ assert(not pcall(function () local a = {[nil] = 10} end))
|
||||
assert(a[nil] == undef)
|
||||
a = nil
|
||||
|
||||
local a, b, c
|
||||
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)
|
||||
@@ -434,6 +436,16 @@ a.aVeryLongName012345678901234567890123456789012345678901234567890123456789 ==
|
||||
10)
|
||||
|
||||
|
||||
do
|
||||
-- _ENV constant
|
||||
local function foo ()
|
||||
local _ENV <const> = 11
|
||||
X = "hi"
|
||||
end
|
||||
local st, msg = pcall(foo)
|
||||
assert(not st and string.find(msg, "number"))
|
||||
end
|
||||
|
||||
|
||||
-- test of large float/integer indices
|
||||
|
||||
@@ -445,7 +457,7 @@ while maxint ~= (maxint + 0.0) or (maxint - 1) ~= (maxint - 1.0) do
|
||||
maxint = maxint // 2
|
||||
end
|
||||
|
||||
maxintF = maxint + 0.0 -- float version
|
||||
local maxintF = maxint + 0.0 -- float version
|
||||
|
||||
assert(maxintF == maxint and math.type(maxintF) == "float" and
|
||||
maxintF >= 2.0^14)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- $Id: testes/big.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
if _soft then
|
||||
return 'a'
|
||||
@@ -32,7 +32,7 @@ setmetatable(env, {
|
||||
})
|
||||
|
||||
X = nil
|
||||
co = coroutine.wrap(f)
|
||||
local co = coroutine.wrap(f)
|
||||
assert(co() == 's')
|
||||
assert(co() == 'g')
|
||||
assert(co() == 'g')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- $Id: testes/bitwise.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
print("testing bitwise operations")
|
||||
|
||||
@@ -38,6 +38,18 @@ d = d << 32
|
||||
assert(a | b ~ c & d == 0xF4000000 << 32)
|
||||
assert(~~a == a and ~a == -1 ~ a and -d == ~d + 1)
|
||||
|
||||
|
||||
do -- constant folding
|
||||
local code = string.format("return -1 >> %d", math.maxinteger)
|
||||
assert(load(code)() == 0)
|
||||
local code = string.format("return -1 >> %d", math.mininteger)
|
||||
assert(load(code)() == 0)
|
||||
local code = string.format("return -1 << %d", math.maxinteger)
|
||||
assert(load(code)() == 0)
|
||||
local code = string.format("return -1 << %d", math.mininteger)
|
||||
assert(load(code)() == 0)
|
||||
end
|
||||
|
||||
assert(-1 >> 1 == (1 << (numbits - 1)) - 1 and 1 << 31 == 0x80000000)
|
||||
assert(-1 >> (numbits - 1) == 1)
|
||||
assert(-1 >> numbits == 0 and
|
||||
@@ -45,6 +57,11 @@ assert(-1 >> numbits == 0 and
|
||||
-1 << numbits == 0 and
|
||||
-1 << -numbits == 0)
|
||||
|
||||
assert(1 >> math.mininteger == 0)
|
||||
assert(1 >> math.maxinteger == 0)
|
||||
assert(1 << math.mininteger == 0)
|
||||
assert(1 << math.maxinteger == 0)
|
||||
|
||||
assert((2^30 - 1) << 2^30 == 0)
|
||||
assert((2^30 - 1) >> 2^30 == 0)
|
||||
|
||||
|
||||
188
testes/calls.lua
188
testes/calls.lua
@@ -1,5 +1,7 @@
|
||||
-- $Id: testes/calls.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
global <const> *
|
||||
|
||||
print("testing functions and calls")
|
||||
|
||||
@@ -16,13 +18,13 @@ assert(type(nil) == 'nil'
|
||||
and type(type) == 'function')
|
||||
|
||||
assert(type(assert) == type(print))
|
||||
function f (x) return a:x (x) end
|
||||
local function f (x) return a:x (x) end
|
||||
assert(type(f) == 'function')
|
||||
assert(not pcall(type))
|
||||
|
||||
|
||||
-- testing local-function recursion
|
||||
fact = false
|
||||
global fact; fact = false
|
||||
do
|
||||
local res = 1
|
||||
local function fact (n)
|
||||
@@ -33,10 +35,11 @@ do
|
||||
assert(fact(5) == 120)
|
||||
end
|
||||
assert(fact == false)
|
||||
fact = nil
|
||||
|
||||
-- testing declarations
|
||||
a = {i = 10}
|
||||
self = 20
|
||||
local a = {i = 10}
|
||||
local self = 20
|
||||
function a:x (x) return x+self.i end
|
||||
function a.y (x) return x+self end
|
||||
|
||||
@@ -62,7 +65,7 @@ a.b.c:f2('k', 12); assert(a.b.c.k == 12)
|
||||
|
||||
print('+')
|
||||
|
||||
t = nil -- 'declare' t
|
||||
global t; 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
|
||||
@@ -72,7 +75,9 @@ 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)
|
||||
t = nil -- delete 't'
|
||||
|
||||
global function fat(x)
|
||||
if x <= 1 then return 1
|
||||
else return x*load("return fat(" .. x-1 .. ")", "")()
|
||||
end
|
||||
@@ -80,28 +85,31 @@ end
|
||||
|
||||
assert(load "load 'assert(fat(6)==720)' () ")()
|
||||
a = load('return fat(5), 3')
|
||||
a,b = a()
|
||||
local a,b = a()
|
||||
assert(a == 120 and b == 3)
|
||||
fat = nil
|
||||
print('+')
|
||||
|
||||
function err_on_n (n)
|
||||
local 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)
|
||||
local function dummy (n)
|
||||
if n > 0 then
|
||||
assert(not pcall(err_on_n, n))
|
||||
dummy(n-1)
|
||||
end
|
||||
end
|
||||
|
||||
dummy(10)
|
||||
end
|
||||
|
||||
dummy(10)
|
||||
_G.deep = nil -- "declaration" (used by 'all.lua')
|
||||
|
||||
function deep (n)
|
||||
global function deep (n)
|
||||
if n>0 then deep(n-1) end
|
||||
end
|
||||
deep(10)
|
||||
@@ -151,6 +159,16 @@ do -- tail calls x varargs
|
||||
end
|
||||
|
||||
|
||||
do -- C-stack overflow while handling C-stack overflow
|
||||
local function loop ()
|
||||
assert(pcall(loop))
|
||||
end
|
||||
|
||||
local err, msg = xpcall(loop, loop)
|
||||
assert(not err and string.find(msg, "error"))
|
||||
end
|
||||
|
||||
|
||||
|
||||
do -- tail calls x chain of __call
|
||||
local n = 10000 -- depth
|
||||
@@ -162,7 +180,7 @@ do -- tail calls x chain of __call
|
||||
end
|
||||
|
||||
-- build a chain of __call metamethods ending in function 'foo'
|
||||
for i = 1, 100 do
|
||||
for i = 1, 15 do
|
||||
foo = setmetatable({}, {__call = foo})
|
||||
end
|
||||
|
||||
@@ -174,8 +192,8 @@ end
|
||||
print('+')
|
||||
|
||||
|
||||
do -- testing chains of '__call'
|
||||
local N = 20
|
||||
do print"testing chains of '__call'"
|
||||
local N = 15
|
||||
local u = table.pack
|
||||
for i = 1, N do
|
||||
u = setmetatable({i}, {__call = u})
|
||||
@@ -188,9 +206,37 @@ do -- testing chains of '__call'
|
||||
assert(Res[i][1] == i)
|
||||
end
|
||||
assert(Res[N + 1] == "a" and Res[N + 2] == "b" and Res[N + 3] == "c")
|
||||
|
||||
local function u (...)
|
||||
local n = debug.getinfo(1, 't').extraargs
|
||||
assert(select("#", ...) == n)
|
||||
return n
|
||||
end
|
||||
|
||||
for i = 0, N do
|
||||
assert(u() == i)
|
||||
u = setmetatable({}, {__call = u})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
do -- testing chains too long
|
||||
local a = {}
|
||||
for i = 1, 16 do -- one too many
|
||||
a = setmetatable({}, {__call = a})
|
||||
end
|
||||
local status, msg = pcall(a)
|
||||
assert(not status and string.find(msg, "too long"))
|
||||
|
||||
setmetatable(a, {__call = a}) -- infinite chain
|
||||
local status, msg = pcall(a)
|
||||
assert(not status and string.find(msg, "too long"))
|
||||
|
||||
-- again, with a tail call
|
||||
local status, msg = pcall(function () return a() end)
|
||||
assert(not status and string.find(msg, "too long"))
|
||||
end
|
||||
|
||||
a = nil
|
||||
(function (x) a=x end)(23)
|
||||
assert(a == 23 and (function (x) return x*2 end)(20) == 40)
|
||||
@@ -199,7 +245,7 @@ assert(a == 23 and (function (x) return x*2 end)(20) == 40)
|
||||
-- testing closures
|
||||
|
||||
-- fixed-point operator
|
||||
Z = function (le)
|
||||
local Z = function (le)
|
||||
local function a (f)
|
||||
return le(function (x) return f(f)(x) end)
|
||||
end
|
||||
@@ -209,14 +255,14 @@ Z = function (le)
|
||||
|
||||
-- non-recursive factorial
|
||||
|
||||
F = function (f)
|
||||
local F = function (f)
|
||||
return function (n)
|
||||
if n == 0 then return 1
|
||||
else return n*f(n-1) end
|
||||
end
|
||||
end
|
||||
|
||||
fat = Z(F)
|
||||
local fat = Z(F)
|
||||
|
||||
assert(fat(0) == 1 and fat(4) == 24 and Z(F)(5)==5*Z(F)(4))
|
||||
|
||||
@@ -227,22 +273,21 @@ local function g (z)
|
||||
return f(z,z+1,z+2,z+3)
|
||||
end
|
||||
|
||||
f = g(10)
|
||||
local 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)
|
||||
local 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)
|
||||
local function equaltab (t1, t2)
|
||||
assert(#t1 == #t2)
|
||||
for i = 1, #t1 do
|
||||
assert(t1[i] == t2[i])
|
||||
@@ -251,8 +296,8 @@ 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 function f() return 1,2,30,4 end
|
||||
local 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)
|
||||
@@ -281,7 +326,7 @@ table.sort({10,9,8,4,19,23,0,0}, function (a,b) return a<b end, "extra arg")
|
||||
local x = "-- a comment\0\0\0\n x = 10 + \n23; \
|
||||
local a = function () x = 'hi' end; \
|
||||
return '\0'"
|
||||
function read1 (x)
|
||||
local function read1 (x)
|
||||
local i = 0
|
||||
return function ()
|
||||
collectgarbage()
|
||||
@@ -290,7 +335,7 @@ function read1 (x)
|
||||
end
|
||||
end
|
||||
|
||||
function cannotload (msg, a,b)
|
||||
local function cannotload (msg, a,b)
|
||||
assert(not a and string.find(b, msg))
|
||||
end
|
||||
|
||||
@@ -309,7 +354,7 @@ assert(not load(function () return true end))
|
||||
|
||||
-- small bug
|
||||
local t = {nil, "return ", "3"}
|
||||
f, msg = load(function () return table.remove(t, 1) end)
|
||||
local 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)
|
||||
@@ -332,6 +377,7 @@ 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"))
|
||||
_G.x = nil
|
||||
|
||||
assert(not pcall(string.dump, print)) -- no dump of C functions
|
||||
|
||||
@@ -344,7 +390,8 @@ assert(load("return _ENV", nil, nil, 123)() == 123)
|
||||
|
||||
|
||||
-- load when _ENV is not first upvalue
|
||||
local x; XX = 123
|
||||
global XX; local x
|
||||
XX = 123
|
||||
local function h ()
|
||||
local y=x -- use 'x', so that it becomes 1st upvalue
|
||||
return XX -- global name
|
||||
@@ -356,7 +403,7 @@ debug.setupvalue(x, 2, _G)
|
||||
assert(x() == 123)
|
||||
|
||||
assert(assert(load("return XX + ...", nil, nil, {XX = 13}))(4) == 17)
|
||||
|
||||
XX = nil
|
||||
|
||||
-- test generic load with nested functions
|
||||
x = [[
|
||||
@@ -436,15 +483,22 @@ assert((function (a) return a end)() == nil)
|
||||
|
||||
print("testing binary chunks")
|
||||
do
|
||||
local header = string.pack("c4BBc6BBB",
|
||||
"\27Lua", -- signature
|
||||
0x54, -- version 5.4 (0x54)
|
||||
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)
|
||||
)
|
||||
local headformat = "c4BBc6BiBI4BjBn"
|
||||
local header = { -- header components
|
||||
"\27Lua", -- signature
|
||||
0x55, -- version 5.5 (0x55)
|
||||
0, -- format
|
||||
"\x19\x93\r\n\x1a\n", -- a binary string
|
||||
string.packsize("i"), -- size of an int
|
||||
-0x5678, -- an int
|
||||
4, -- size of an instruction
|
||||
0x12345678, -- an instruction (4 bytes)
|
||||
string.packsize("j"), -- size of a Lua integer
|
||||
-0x5678, -- a Lua integer
|
||||
string.packsize("n"), -- size of a Lua float
|
||||
-370.5, -- a Lua float
|
||||
}
|
||||
|
||||
local c = string.dump(function ()
|
||||
local a = 1; local b = 3;
|
||||
local f = function () return a + b + _ENV.c; end -- upvalues
|
||||
@@ -456,17 +510,23 @@ do
|
||||
assert(assert(load(c))() == 10)
|
||||
|
||||
-- check header
|
||||
assert(string.sub(c, 1, #header) == header)
|
||||
-- check LUAC_INT and LUAC_NUM
|
||||
local ci, cn = string.unpack("jn", c, #header + 1)
|
||||
assert(ci == 0x5678 and cn == 370.5)
|
||||
|
||||
-- corrupted header
|
||||
local t = {string.unpack(headformat, c)}
|
||||
for i = 1, #header do
|
||||
assert(t[i] == header[i])
|
||||
end
|
||||
|
||||
-- Testing corrupted header.
|
||||
-- A single wrong byte in the head invalidates the chunk,
|
||||
-- except for the Lua float check. (If numbers are long double,
|
||||
-- the representation may need padding, and changing that padding
|
||||
-- will not invalidate the chunk.)
|
||||
local headlen = string.packsize(headformat)
|
||||
headlen = headlen - string.packsize("n") -- remove float check
|
||||
for i = 1, headlen do
|
||||
local s = string.sub(c, 1, i - 1) ..
|
||||
string.char(string.byte(string.sub(c, i, i)) + 1) ..
|
||||
string.char((string.byte(string.sub(c, i, i)) + 1) & 0xFF) ..
|
||||
string.sub(c, i + 1, -1)
|
||||
assert(#s == #c)
|
||||
assert(#s == #c and s ~= c)
|
||||
assert(not load(s))
|
||||
end
|
||||
|
||||
@@ -477,5 +537,41 @@ do
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
do -- check reuse of strings in dumps
|
||||
local str = "|" .. string.rep("X", 50) .. "|"
|
||||
local foo = load(string.format([[
|
||||
local str <const> = "%s"
|
||||
return {
|
||||
function () return str end,
|
||||
function () return str end,
|
||||
function () return str end
|
||||
}
|
||||
]], str))
|
||||
-- count occurrences of 'str' inside the dump
|
||||
local dump = string.dump(foo)
|
||||
local _, count = string.gsub(dump, str, {})
|
||||
-- there should be only two occurrences:
|
||||
-- one inside the source, other the string itself.
|
||||
assert(count == 2)
|
||||
|
||||
if T then -- check reuse of strings in undump
|
||||
local funcs = load(dump)()
|
||||
assert(string.format("%p", T.listk(funcs[1])[1]) ==
|
||||
string.format("%p", T.listk(funcs[3])[1]))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
do -- test limit of multiple returns (254 values)
|
||||
local code = "return 10" .. string.rep(",10", 253)
|
||||
local res = {assert(load(code))()}
|
||||
assert(#res == 254 and res[254] == 10)
|
||||
|
||||
code = code .. ",10"
|
||||
local status, msg = load(code)
|
||||
assert(not status and string.find(msg, "too many returns"))
|
||||
end
|
||||
|
||||
print('OK')
|
||||
return deep
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
-- $Id: testes/closure.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
global <const> *
|
||||
|
||||
print "testing closures"
|
||||
|
||||
do -- bug in 5.4.7
|
||||
_ENV[true] = 10
|
||||
local function aux () return _ENV[1 < 2] end
|
||||
assert(aux() == 10)
|
||||
_ENV[true] = nil
|
||||
end
|
||||
|
||||
|
||||
local A,B = 0,{g=10}
|
||||
function f(x)
|
||||
local function f(x)
|
||||
local a = {}
|
||||
for i=1,1000 do
|
||||
local y = 0
|
||||
@@ -60,35 +70,33 @@ 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}
|
||||
a[i] = 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')
|
||||
assert(a[2]() == 2)
|
||||
assert(a[3]() == 3)
|
||||
|
||||
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,
|
||||
a[i] = {set = function(x) k=x end,
|
||||
get = function () return i, k end}
|
||||
if i == 2 then break end
|
||||
end
|
||||
a[1].set(10, 20)
|
||||
a[1].set(10)
|
||||
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')
|
||||
assert(r == 1 and s == 10)
|
||||
a[2].set('a')
|
||||
r,s = a[2].get()
|
||||
assert(r == "a" and s == "b")
|
||||
assert(r == 2 and s == "a")
|
||||
|
||||
|
||||
-- testing closures with 'for' control variable x break
|
||||
local f
|
||||
for i=1,3 do
|
||||
f = function () return i end
|
||||
break
|
||||
@@ -139,7 +147,7 @@ assert(b('get') == 'xuxu')
|
||||
b('set', 10); assert(b('get') == 14)
|
||||
|
||||
|
||||
local w
|
||||
local y, w
|
||||
-- testing multi-level closure
|
||||
function f(x)
|
||||
return function (y)
|
||||
@@ -230,6 +238,7 @@ t()
|
||||
-- test for debug manipulation of upvalues
|
||||
local debug = require'debug'
|
||||
|
||||
local foo1, foo2, foo3
|
||||
do
|
||||
local a , b, c = 3, 5, 7
|
||||
foo1 = function () return a+b end;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
-- $Id: testes/code.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
global <const> *
|
||||
|
||||
if T==nil then
|
||||
(Message or print)('\n >>> testC not active: skipping opcode tests <<<\n')
|
||||
@@ -69,10 +71,24 @@ foo = function (f, a)
|
||||
checkKlist(foo, {100000, 100000.0, -100000, -100000.0})
|
||||
|
||||
|
||||
-- floats x integers
|
||||
foo = function (t, a)
|
||||
t[a] = 1; t[a] = 1.0
|
||||
t[a] = 1; t[a] = 1.0
|
||||
t[a] = 2; t[a] = 2.0
|
||||
t[a] = 0; t[a] = 0.0
|
||||
t[a] = 1; t[a] = 1.0
|
||||
t[a] = 2; t[a] = 2.0
|
||||
t[a] = 0; t[a] = 0.0
|
||||
end
|
||||
|
||||
checkKlist(foo, {1, 1.0, 2, 2.0, 0, 0.0})
|
||||
|
||||
|
||||
-- testing opcodes
|
||||
|
||||
-- check that 'f' opcodes match '...'
|
||||
function check (f, ...)
|
||||
local function check (f, ...)
|
||||
local arg = {...}
|
||||
local c = T.listcode(f)
|
||||
for i=1, #arg do
|
||||
@@ -85,7 +101,7 @@ end
|
||||
|
||||
|
||||
-- check that 'f' opcodes match '...' and that 'f(p) == r'.
|
||||
function checkR (f, p, r, ...)
|
||||
local function checkR (f, p, r, ...)
|
||||
local r1 = f(p)
|
||||
assert(r == r1 and math.type(r) == math.type(r1))
|
||||
check(f, ...)
|
||||
@@ -93,7 +109,7 @@ end
|
||||
|
||||
|
||||
-- check that 'a' and 'b' has the same opcodes
|
||||
function checkequal (a, b)
|
||||
local function checkequal (a, b)
|
||||
a = T.listcode(a)
|
||||
b = T.listcode(b)
|
||||
assert(#a == #b)
|
||||
@@ -391,20 +407,29 @@ do -- tests for table access in upvalues
|
||||
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 () local a, b; if not (a or b) then b=a end end,
|
||||
function () local a, b; 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')
|
||||
'TEST', 'JMP', 'TEST', 'JMP', 'JMP', 'CLOSE', 'JMP', 'ADDI', 'MMBINI', 'JMP', 'RETURN0')
|
||||
|
||||
check(function ()
|
||||
do
|
||||
goto exit -- don't need to close
|
||||
local x <close> = nil
|
||||
goto exit -- must close
|
||||
end
|
||||
::exit::
|
||||
end, 'JMP', 'CLOSE', 'LOADNIL', 'TBC',
|
||||
'CLOSE', 'JMP', 'CLOSE', 'RETURN')
|
||||
|
||||
checkequal(function () return 6 or true or nil end,
|
||||
function () return k6 or kTrue or kNil end)
|
||||
@@ -431,5 +456,49 @@ do -- string constants
|
||||
assert(T.listk(f2)[1] == nil)
|
||||
end
|
||||
|
||||
|
||||
do -- check number of available registers
|
||||
-- 1 register for local + 1 for function + 252 arguments
|
||||
local source = "local a; return a(" .. string.rep("a, ", 252) .. "a)"
|
||||
local prog = T.listcode(assert(load(source)))
|
||||
-- maximum valid register is 254
|
||||
for i = 1, 254 do
|
||||
assert(string.find(prog[2 + i], "MOVE%s*" .. i))
|
||||
end
|
||||
-- one more argument would need register #255 (but that is reserved)
|
||||
source = "local a; return a(" .. string.rep("a, ", 253) .. "a)"
|
||||
local _, msg = load(source)
|
||||
assert(string.find(msg, "too many registers"))
|
||||
end
|
||||
|
||||
|
||||
do -- basic check for SETLIST
|
||||
-- create a list constructor with 50 elements
|
||||
local source = "local a; return {" .. string.rep("a, ", 50) .. "}"
|
||||
local func = assert(load(source))
|
||||
local code = table.concat(T.listcode(func), "\n")
|
||||
local _, count = string.gsub(code, "SETLIST", "")
|
||||
-- code uses only 1 SETLIST for the constructor
|
||||
assert(count == 1)
|
||||
end
|
||||
|
||||
|
||||
do print("testing code for integer limits")
|
||||
local function checkints (n)
|
||||
local source = string.format(
|
||||
"local a = {[true] = 0X%x}; return a[true]", n)
|
||||
local f = assert(load(source))
|
||||
checkKlist(f, {n})
|
||||
assert(f() == n)
|
||||
f = load(string.dump(f))
|
||||
assert(f() == n)
|
||||
end
|
||||
|
||||
checkints(math.maxinteger)
|
||||
checkints(math.mininteger)
|
||||
checkints(-1)
|
||||
|
||||
end
|
||||
|
||||
print 'OK'
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- $Id: testes/constructs.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
;;print "testing syntax";;
|
||||
|
||||
@@ -11,6 +11,7 @@ local function checkload (s, msg)
|
||||
end
|
||||
|
||||
-- testing semicollons
|
||||
local a
|
||||
do ;;; end
|
||||
; do ; a = 3; assert(a == 3) end;
|
||||
;
|
||||
@@ -49,17 +50,17 @@ 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);
|
||||
local 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;
|
||||
local 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
|
||||
do -- testing operators with different kinds of constants
|
||||
-- operands to consider:
|
||||
-- * fit in register
|
||||
-- * constant doesn't fit in register
|
||||
@@ -77,13 +78,13 @@ do -- testing operators with diffent kinds of constants
|
||||
local gab = f(o1, o2)
|
||||
|
||||
_ENV.XX = o1
|
||||
code = string.format("return XX %s %s", op, o2)
|
||||
res = assert(load(code))()
|
||||
local code = string.format("return XX %s %s", op, o2)
|
||||
local 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))()
|
||||
code = string.format("return (%s) %s XX", o1, op)
|
||||
res = assert(load(code))()
|
||||
assert(res == gab)
|
||||
|
||||
code = string.format("return (%s) %s %s", o1, op, o2)
|
||||
@@ -92,6 +93,7 @@ do -- testing operators with diffent kinds of constants
|
||||
end
|
||||
end
|
||||
end
|
||||
_ENV.XX = nil
|
||||
end
|
||||
|
||||
|
||||
@@ -100,10 +102,35 @@ 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
|
||||
local a; local function f(x) x={a=1}; x={x=1}; x={G=1} end
|
||||
end
|
||||
|
||||
function f (i)
|
||||
|
||||
do -- bug since 5.4.0
|
||||
-- create code with a table using more than 256 constants
|
||||
local code = {"local x = {"}
|
||||
for i = 1, 257 do
|
||||
code[#code + 1] = i .. ".1,"
|
||||
end
|
||||
code[#code + 1] = "};"
|
||||
code = table.concat(code)
|
||||
|
||||
-- add "ret" to the end of that code and checks that
|
||||
-- it produces the expected value "val"
|
||||
local function check (ret, val)
|
||||
local code = code .. ret
|
||||
code = load(code)
|
||||
assert(code() == val)
|
||||
end
|
||||
|
||||
check("return (1 ~ (2 or 3))", 1 ~ 2)
|
||||
check("return (1 | (2 or 3))", 1 | 2)
|
||||
check("return (1 + (2 or 3))", 1 + 2)
|
||||
check("return (1 << (2 or 3))", 1 << 2)
|
||||
end
|
||||
|
||||
|
||||
local function f (i)
|
||||
if type(i) ~= 'number' then return i,'jojo'; end;
|
||||
if i > 0 then return i, f(i-1); end;
|
||||
end
|
||||
@@ -129,10 +156,10 @@ 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
|
||||
local n=100;
|
||||
local i=3;
|
||||
local t = {};
|
||||
local 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
|
||||
@@ -175,14 +202,14 @@ a={y=1}
|
||||
x = {a.y}
|
||||
assert(x[1] == 1)
|
||||
|
||||
function f(i)
|
||||
local function f (i)
|
||||
while 1 do
|
||||
if i>0 then i=i-1;
|
||||
else return; end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function g(i)
|
||||
local function g(i)
|
||||
while 1 do
|
||||
if i>0 then i=i-1
|
||||
else return end
|
||||
@@ -247,7 +274,7 @@ 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)
|
||||
local 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;
|
||||
@@ -275,7 +302,7 @@ do
|
||||
assert(a==2)
|
||||
end
|
||||
|
||||
function F(a)
|
||||
local function F (a)
|
||||
assert(debug.getinfo(1, "n").name == 'F')
|
||||
return a,2,3
|
||||
end
|
||||
@@ -368,6 +395,8 @@ for n = 1, level do
|
||||
if i % 60000 == 0 then print('+') end
|
||||
end
|
||||
end
|
||||
IX = nil
|
||||
_G.GLOB1 = nil
|
||||
------------------------------------------------------------------
|
||||
|
||||
-- testing some syntax errors (chosen through 'gcov')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- $Id: testes/coroutine.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
print "testing coroutines"
|
||||
|
||||
@@ -30,7 +30,8 @@ local function eqtab (t1, t2)
|
||||
end
|
||||
|
||||
_G.x = nil -- declare x
|
||||
function foo (a, ...)
|
||||
_G.f = nil -- declare f
|
||||
local function foo (a, ...)
|
||||
local x, y = coroutine.running()
|
||||
assert(x == f and y == false)
|
||||
-- next call should not corrupt coroutine (but must fail,
|
||||
@@ -67,10 +68,11 @@ assert(coroutine.status(f) == "dead")
|
||||
s, a = coroutine.resume(f, "xuxu")
|
||||
assert(not s and string.find(a, "dead") and coroutine.status(f) == "dead")
|
||||
|
||||
_G.f = nil
|
||||
|
||||
-- yields in tail calls
|
||||
local function foo (i) return coroutine.yield(i) end
|
||||
f = coroutine.wrap(function ()
|
||||
local f = coroutine.wrap(function ()
|
||||
for i=1,10 do
|
||||
assert(foo(i) == _G.x)
|
||||
end
|
||||
@@ -79,8 +81,10 @@ end)
|
||||
for i=1,10 do _G.x = i; assert(f(i) == i) end
|
||||
_G.x = 'xuxu'; assert(f('xuxu') == 'a')
|
||||
|
||||
_G.x = nil
|
||||
|
||||
-- recursive
|
||||
function pf (n, i)
|
||||
local function pf (n, i)
|
||||
coroutine.yield(n)
|
||||
pf(n*i, i+1)
|
||||
end
|
||||
@@ -93,14 +97,14 @@ for i=1,10 do
|
||||
end
|
||||
|
||||
-- sieve
|
||||
function gen (n)
|
||||
local function gen (n)
|
||||
return coroutine.wrap(function ()
|
||||
for i=2,n do coroutine.yield(i) end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
function filter (p, g)
|
||||
local function filter (p, g)
|
||||
return coroutine.wrap(function ()
|
||||
while 1 do
|
||||
local n = g()
|
||||
@@ -123,6 +127,18 @@ assert(#a == 22 and a[#a] == 79)
|
||||
x, a = nil
|
||||
|
||||
|
||||
do -- "bug" in 5.4.2
|
||||
local function foo () foo () end -- just create a stack overflow
|
||||
local co = coroutine.create(foo)
|
||||
-- running this coroutine would overflow the unsigned short 'nci', the
|
||||
-- counter of CallInfo structures available to the thread.
|
||||
-- (The issue only manifests in an 'assert'.)
|
||||
local st, msg = coroutine.resume(co)
|
||||
assert(string.find(msg, "stack overflow"))
|
||||
assert(coroutine.status(co) == "dead")
|
||||
end
|
||||
|
||||
|
||||
print("to-be-closed variables in coroutines")
|
||||
|
||||
local function func2close (f)
|
||||
@@ -136,19 +152,38 @@ do
|
||||
assert(coroutine.status(co) == "dead")
|
||||
local st, msg = coroutine.close(co)
|
||||
assert(st and msg == nil)
|
||||
|
||||
-- cannot close the running coroutine
|
||||
local st, msg = pcall(coroutine.close, coroutine.running())
|
||||
assert(not st and string.find(msg, "running"))
|
||||
-- also ok to close it again
|
||||
st, msg = coroutine.close(co)
|
||||
assert(st and msg == nil)
|
||||
|
||||
local main = coroutine.running()
|
||||
|
||||
-- cannot close 'main'
|
||||
local st, msg = pcall(coroutine.close, main);
|
||||
assert(not st and string.find(msg, "main"))
|
||||
|
||||
|
||||
-- cannot close a "normal" coroutine
|
||||
;(coroutine.wrap(function ()
|
||||
local st, msg = pcall(coroutine.close, main)
|
||||
assert(not st and string.find(msg, "normal"))
|
||||
end))()
|
||||
|
||||
do -- close a coroutine while closing it
|
||||
local co
|
||||
co = coroutine.create(
|
||||
function()
|
||||
local x <close> = func2close(function()
|
||||
coroutine.close(co) -- close it again
|
||||
end)
|
||||
coroutine.yield(20)
|
||||
end)
|
||||
local st, msg = coroutine.resume(co)
|
||||
assert(st and msg == 20)
|
||||
st, msg = coroutine.close(co)
|
||||
assert(st and msg == nil)
|
||||
end
|
||||
|
||||
-- to-be-closed variables in coroutines
|
||||
local X
|
||||
|
||||
@@ -158,6 +193,9 @@ do
|
||||
assert(not st and msg == 100)
|
||||
st, msg = coroutine.close(co)
|
||||
assert(not st and msg == 100)
|
||||
-- after closing, no more errors
|
||||
st, msg = coroutine.close(co)
|
||||
assert(st and msg == nil)
|
||||
|
||||
co = coroutine.create(function ()
|
||||
local x <close> = func2close(function (self, err)
|
||||
@@ -189,23 +227,26 @@ do
|
||||
local st, msg = coroutine.close(co)
|
||||
assert(st == false and coroutine.status(co) == "dead" and msg == 200)
|
||||
assert(x == 200)
|
||||
-- after closing, no more errors
|
||||
st, msg = coroutine.close(co)
|
||||
assert(st and msg == nil)
|
||||
end
|
||||
|
||||
do
|
||||
-- <close> versus pcall in coroutines
|
||||
local X = false
|
||||
local Y = false
|
||||
function foo ()
|
||||
local function foo ()
|
||||
local x <close> = func2close(function (self, err)
|
||||
Y = debug.getinfo(2)
|
||||
X = err
|
||||
end)
|
||||
error(43)
|
||||
end
|
||||
co = coroutine.create(function () return pcall(foo) end)
|
||||
local co = coroutine.create(function () return pcall(foo) end)
|
||||
local st1, st2, err = coroutine.resume(co)
|
||||
assert(st1 and not st2 and err == 43)
|
||||
assert(X == 43 and Y.name == "pcall")
|
||||
assert(X == 43 and Y.what == "C")
|
||||
|
||||
-- recovering from errors in __close metamethods
|
||||
local track = {}
|
||||
@@ -247,9 +288,59 @@ do
|
||||
end
|
||||
|
||||
|
||||
do print("coroutines closing itself")
|
||||
global <const> coroutine, string, os
|
||||
global <const> assert, error, pcall
|
||||
|
||||
local X = nil
|
||||
|
||||
local function new ()
|
||||
return coroutine.create(function (what)
|
||||
|
||||
local <close>var = func2close(function (t, err)
|
||||
if what == "yield" then
|
||||
coroutine.yield()
|
||||
elseif what == "error" then
|
||||
error(200)
|
||||
else
|
||||
X = "Ok"
|
||||
return X
|
||||
end
|
||||
end)
|
||||
|
||||
-- do an unprotected call so that coroutine becomes non-yieldable
|
||||
string.gsub("a", "a", function ()
|
||||
assert(not coroutine.isyieldable())
|
||||
-- do protected calls while non-yieldable, to add recovery
|
||||
-- entries (setjmp) to the stack
|
||||
assert(pcall(pcall, function ()
|
||||
-- 'close' works even while non-yieldable
|
||||
coroutine.close() -- close itself
|
||||
os.exit(false) -- not reacheable
|
||||
end))
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
local co = new()
|
||||
local st, msg = coroutine.resume(co, "ret")
|
||||
assert(st and msg == nil)
|
||||
assert(X == "Ok")
|
||||
|
||||
local co = new()
|
||||
local st, msg = coroutine.resume(co, "error")
|
||||
assert(not st and msg == 200)
|
||||
|
||||
local co = new()
|
||||
local st, msg = coroutine.resume(co, "yield")
|
||||
assert(not st and string.find(msg, "attempt to yield"))
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- yielding across C boundaries
|
||||
|
||||
co = coroutine.wrap(function()
|
||||
local co = coroutine.wrap(function()
|
||||
assert(not pcall(table.sort,{1,2,3}, coroutine.yield))
|
||||
assert(coroutine.isyieldable())
|
||||
coroutine.yield(20)
|
||||
@@ -277,15 +368,15 @@ local r1, r2, v = f1(nil)
|
||||
assert(r1 and not r2 and v[1] == (10 + 1)*10/2)
|
||||
|
||||
|
||||
function f (a, b) a = coroutine.yield(a); error{a + b} end
|
||||
function g(x) return x[1]*2 end
|
||||
local function f (a, b) a = coroutine.yield(a); error{a + b} end
|
||||
local function g(x) return x[1]*2 end
|
||||
|
||||
co = coroutine.wrap(function ()
|
||||
coroutine.yield(xpcall(f, g, 10, 20))
|
||||
end)
|
||||
|
||||
assert(co() == 10)
|
||||
r, msg = co(100)
|
||||
local r, msg = co(100)
|
||||
assert(not r and msg == 240)
|
||||
|
||||
|
||||
@@ -347,9 +438,10 @@ assert(not a and b == foo and coroutine.status(x) == "dead")
|
||||
a,b = coroutine.resume(x)
|
||||
assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead")
|
||||
|
||||
goo = nil
|
||||
|
||||
-- co-routines x for loop
|
||||
function all (a, n, k)
|
||||
local function all (a, n, k)
|
||||
if k == 0 then coroutine.yield(a)
|
||||
else
|
||||
for i=1,n do
|
||||
@@ -389,7 +481,7 @@ assert(f() == 43 and f() == 53)
|
||||
|
||||
-- old bug: attempt to resume itself
|
||||
|
||||
function co_func (current_co)
|
||||
local function co_func (current_co)
|
||||
assert(coroutine.running() == current_co)
|
||||
assert(coroutine.resume(current_co) == false)
|
||||
coroutine.yield(10, 20)
|
||||
@@ -419,7 +511,7 @@ do
|
||||
|
||||
local X = false
|
||||
A = coroutine.wrap(function()
|
||||
local _ <close> = setmetatable({}, {__close = function () X = true end})
|
||||
local _ <close> = func2close(function () X = true end)
|
||||
return pcall(A, 1)
|
||||
end)
|
||||
st, res = A()
|
||||
@@ -427,6 +519,22 @@ do
|
||||
end
|
||||
|
||||
|
||||
-- bug in 5.4.1
|
||||
do
|
||||
-- coroutine ran close metamethods with invalid status during a
|
||||
-- reset.
|
||||
local co
|
||||
co = coroutine.wrap(function()
|
||||
local x <close> = func2close(function() return pcall(co) end)
|
||||
error(111)
|
||||
end)
|
||||
local st, errobj = pcall(co)
|
||||
assert(not st and errobj == 111)
|
||||
st, errobj = pcall(co)
|
||||
assert(not st and string.find(errobj, "dead coroutine"))
|
||||
end
|
||||
|
||||
|
||||
-- attempt to resume 'normal' coroutine
|
||||
local co1, co2
|
||||
co1 = coroutine.create(function () return co2() end)
|
||||
@@ -446,18 +554,38 @@ assert(not pcall(a, a))
|
||||
a = nil
|
||||
|
||||
|
||||
do
|
||||
-- bug in 5.4: thread can use message handler higher in the stack
|
||||
-- than the variable being closed
|
||||
local c = coroutine.create(function()
|
||||
local clo <close> = setmetatable({}, {__close=function()
|
||||
local x = 134 -- will overwrite message handler
|
||||
error(x)
|
||||
end})
|
||||
-- yields coroutine but leaves a new message handler for it,
|
||||
-- that would be used when closing the coroutine (except that it
|
||||
-- will be overwritten)
|
||||
xpcall(coroutine.yield, function() return "XXX" end)
|
||||
end)
|
||||
|
||||
assert(coroutine.resume(c)) -- start coroutine
|
||||
local st, msg = coroutine.close(c)
|
||||
assert(not st and msg == 134)
|
||||
end
|
||||
|
||||
-- access to locals of erroneous coroutines
|
||||
local x = coroutine.create (function ()
|
||||
local a = 10
|
||||
_G.f = function () a=a+1; return a end
|
||||
_G.F = function () a=a+1; return a end
|
||||
error('x')
|
||||
end)
|
||||
|
||||
assert(not coroutine.resume(x))
|
||||
-- overwrite previous position of local `a'
|
||||
assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1))
|
||||
assert(_G.f() == 11)
|
||||
assert(_G.f() == 12)
|
||||
assert(_G.F() == 11)
|
||||
assert(_G.F() == 12)
|
||||
_G.F = nil
|
||||
|
||||
|
||||
if not T then
|
||||
@@ -467,8 +595,8 @@ else
|
||||
print "testing yields inside hooks"
|
||||
|
||||
local turn
|
||||
|
||||
function fact (t, x)
|
||||
|
||||
local function fact (t, x)
|
||||
assert(turn == t)
|
||||
if x == 0 then return 1
|
||||
else return x*fact(t, x-1)
|
||||
@@ -537,6 +665,7 @@ else
|
||||
_G.X = nil; co(); assert(_G.X == line + 2 and _G.XX == nil)
|
||||
_G.X = nil; co(); assert(_G.X == line + 3 and _G.XX == 20)
|
||||
assert(co() == 10)
|
||||
_G.X = nil
|
||||
|
||||
-- testing yields in count hook
|
||||
co = coroutine.wrap(function ()
|
||||
@@ -561,18 +690,20 @@ else
|
||||
-- (bug in 5.2/5.3)
|
||||
c = coroutine.create(function (a, ...)
|
||||
T.sethook("yield 0", "l") -- will yield on next two lines
|
||||
assert(a == 10)
|
||||
local b = a
|
||||
return ...
|
||||
end)
|
||||
|
||||
assert(coroutine.resume(c, 1, 2, 3)) -- start coroutine
|
||||
local n,v = debug.getlocal(c, 0, 1) -- check its local
|
||||
assert(n == "a" and v == 1)
|
||||
assert(n == "a" and v == 1 and debug.getlocal(c, 0, 2) ~= "b")
|
||||
assert(debug.setlocal(c, 0, 1, 10)) -- test 'setlocal'
|
||||
local t = debug.getinfo(c, 0) -- test 'getinfo'
|
||||
assert(t.currentline == t.linedefined + 1)
|
||||
assert(t.currentline == t.linedefined + 2)
|
||||
assert(not debug.getinfo(c, 1)) -- no other level
|
||||
assert(coroutine.resume(c)) -- run next line
|
||||
local n,v = debug.getlocal(c, 0, 2) -- check next local
|
||||
assert(n == "b" and v == 10)
|
||||
v = {coroutine.resume(c)} -- finish coroutine
|
||||
assert(v[1] == true and v[2] == 2 and v[3] == 3 and v[4] == undef)
|
||||
assert(not coroutine.resume(c))
|
||||
@@ -591,7 +722,7 @@ else
|
||||
|
||||
|
||||
print "testing coroutine API"
|
||||
|
||||
|
||||
-- reusing a thread
|
||||
assert(T.testC([[
|
||||
newthread # create thread
|
||||
@@ -614,6 +745,8 @@ else
|
||||
|
||||
assert(X == 'a a a' and Y == 'OK')
|
||||
|
||||
X, Y = nil
|
||||
|
||||
|
||||
-- resuming running coroutine
|
||||
C = coroutine.create(function ()
|
||||
@@ -630,7 +763,7 @@ else
|
||||
c == "ERRRUN" and d == 4)
|
||||
|
||||
a, b, c, d = T.testC([[
|
||||
rawgeti R 1 # get main thread
|
||||
rawgeti R !M # get main thread
|
||||
pushnum 10;
|
||||
pushnum 20;
|
||||
resume -3 2;
|
||||
@@ -648,19 +781,19 @@ else
|
||||
assert(T.testC(state, "newthread; isyieldable -1; remove 1; return 1"))
|
||||
|
||||
-- main thread is not yieldable
|
||||
assert(not T.testC(state, "rawgeti R 1; isyieldable -1; remove 1; return 1"))
|
||||
assert(not T.testC(state, "rawgeti R !M; isyieldable -1; remove 1; return 1"))
|
||||
|
||||
T.testC(state, "settop 0")
|
||||
|
||||
T.loadlib(state)
|
||||
T.loadlib(state, 1 | 2, 4) -- load _G and 'package', preload 'coroutine'
|
||||
|
||||
assert(T.doremote(state, [[
|
||||
coroutine = require'coroutine';
|
||||
X = function (x) coroutine.yield(x, 'BB'); return 'CC' end;
|
||||
return 'ok']]))
|
||||
|
||||
t = table.pack(T.testC(state, [[
|
||||
rawgeti R 1 # get main thread
|
||||
local t = table.pack(T.testC(state, [[
|
||||
rawgeti R !M # get main thread
|
||||
pushstring 'XX'
|
||||
getglobal X # get function for body
|
||||
pushstring AA # arg
|
||||
@@ -669,7 +802,7 @@ else
|
||||
setglobal T # top
|
||||
setglobal B # second yielded value
|
||||
setglobal A # fist yielded value
|
||||
rawgeti R 1 # get main thread
|
||||
rawgeti R !M # get main thread
|
||||
pushnum 5 # arg (noise)
|
||||
resume 1 1 # after coroutine ends, previous stack is back
|
||||
pushstatus
|
||||
@@ -688,31 +821,28 @@ end
|
||||
|
||||
|
||||
-- leaving a pending coroutine open
|
||||
_X = coroutine.wrap(function ()
|
||||
_G.TO_SURVIVE = coroutine.wrap(function ()
|
||||
local a = 10
|
||||
local x = function () a = a+1 end
|
||||
coroutine.yield()
|
||||
end)
|
||||
|
||||
_X()
|
||||
_G.TO_SURVIVE()
|
||||
|
||||
|
||||
if not _soft then
|
||||
-- bug (stack overflow)
|
||||
local j = 2^9
|
||||
local lim = 1000000 -- (C stack limit; assume 32-bit machine)
|
||||
local t = {lim - 10, lim - 5, lim - 1, lim, lim + 1}
|
||||
local lim = 1000000 -- stack limit; assume 32-bit machine
|
||||
local t = {lim - 10, lim - 5, lim - 1, lim, lim + 1, lim + 5}
|
||||
for i = 1, #t do
|
||||
local j = t[i]
|
||||
co = coroutine.create(function()
|
||||
local t = {}
|
||||
for i = 1, j do t[i] = i end
|
||||
return table.unpack(t)
|
||||
local co = coroutine.create(function()
|
||||
return table.unpack({}, 1, j)
|
||||
end)
|
||||
local r, msg = coroutine.resume(co)
|
||||
assert(not r)
|
||||
-- must fail for unpacking larger than stack limit
|
||||
assert(j < lim or not r)
|
||||
end
|
||||
co = nil
|
||||
end
|
||||
|
||||
|
||||
@@ -870,7 +1000,7 @@ do -- a few more tests for comparison operators
|
||||
until res ~= 10
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
local function test ()
|
||||
local a1 = setmetatable({x=1}, mt1)
|
||||
local a2 = setmetatable({x=2}, mt2)
|
||||
@@ -882,7 +1012,7 @@ do -- a few more tests for comparison operators
|
||||
assert(2 >= a2)
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
run(test)
|
||||
|
||||
end
|
||||
@@ -896,7 +1026,7 @@ assert(run(function ()
|
||||
do local _ENV = _ENV
|
||||
f = function () AAA = BBB + 1; return AAA end
|
||||
end
|
||||
g = new(10); g.k.BBB = 10;
|
||||
local g = new(10); g.k.BBB = 10;
|
||||
debug.setupvalue(f, 1, g)
|
||||
assert(run(f, {"idx", "nidx", "idx"}) == 11)
|
||||
assert(g.k.AAA == 11)
|
||||
@@ -987,6 +1117,31 @@ f = T.makeCfunc([[
|
||||
return *
|
||||
]], 23, "huu")
|
||||
|
||||
|
||||
do -- testing bug introduced in commit f407b3c4a
|
||||
local X = false -- flag "to be closed"
|
||||
local coro = coroutine.wrap(T.testC)
|
||||
-- runs it until 'pcallk' (that yields)
|
||||
-- 4th argument (at index 4): object to be closed
|
||||
local res1, res2 = coro(
|
||||
[[
|
||||
toclose 3 # this could break the next 'pcallk'
|
||||
pushvalue 2 # push function 'yield' to call it
|
||||
pushint 22; pushint 33 # arguments to yield
|
||||
# calls 'yield' (2 args; 2 results; continuation function at index 4)
|
||||
pcallk 2 2 4
|
||||
invalid command (should not arrive here)
|
||||
]], -- 1st argument (at index 1): code;
|
||||
coroutine.yield, -- (at index 2): function to be called
|
||||
func2close(function () X = true end), -- (index 3): TBC slot
|
||||
"pushint 43; return 3" -- (index 4): code for continuation function
|
||||
)
|
||||
|
||||
assert(res1 == 22 and res2 == 33 and not X)
|
||||
local res1, res2, res3 = coro(34, "hi") -- runs continuation function
|
||||
assert(res1 == 34 and res2 == "hi" and res3 == 43 and X)
|
||||
end
|
||||
|
||||
x = coroutine.wrap(f)
|
||||
assert(x() == 102)
|
||||
eqtab({x()}, {23, "huu"})
|
||||
@@ -1036,17 +1191,19 @@ assert(#a == 3 and a[1] == a[2] and a[2] == a[3] and a[3] == 34)
|
||||
|
||||
-- testing yields with continuations
|
||||
|
||||
local y
|
||||
|
||||
co = coroutine.wrap(function (...) return
|
||||
T.testC([[ # initial function
|
||||
yieldk 1 2
|
||||
cannot be here!
|
||||
]],
|
||||
[[ # 1st continuation
|
||||
yieldk 0 3
|
||||
yieldk 0 3
|
||||
cannot be here!
|
||||
]],
|
||||
[[ # 2nd continuation
|
||||
yieldk 0 4
|
||||
yieldk 0 4
|
||||
cannot be here!
|
||||
]],
|
||||
[[ # 3th continuation
|
||||
@@ -1088,6 +1245,9 @@ assert(x == "YIELD" and y == 4)
|
||||
|
||||
assert(not pcall(co)) -- coroutine should be dead
|
||||
|
||||
_G.ctx = nil
|
||||
_G.status = nil
|
||||
|
||||
|
||||
-- bug in nCcalls
|
||||
local co = coroutine.wrap(function ()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- $Id: testes/cstack.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
|
||||
local tracegc = require"tracegc"
|
||||
@@ -84,6 +84,32 @@ do -- bug in 5.4.0
|
||||
end
|
||||
|
||||
|
||||
do -- bug since 5.4.0
|
||||
local count = 0
|
||||
print("chain of 'coroutine.close'")
|
||||
-- create N coroutines forming a list so that each one, when closed,
|
||||
-- closes the previous one. (With a large enough N, previous Lua
|
||||
-- versions crash in this test.)
|
||||
local coro = false
|
||||
for i = 1, 1000 do
|
||||
local previous = coro
|
||||
coro = coroutine.create(function()
|
||||
local cc <close> = setmetatable({}, {__close=function()
|
||||
count = count + 1
|
||||
if previous then
|
||||
assert(coroutine.close(previous))
|
||||
end
|
||||
end})
|
||||
coroutine.yield() -- leaves 'cc' pending to be closed
|
||||
end)
|
||||
assert(coroutine.resume(coro)) -- start it and run until it yields
|
||||
end
|
||||
local st, msg = coroutine.close(coro)
|
||||
assert(not st and string.find(msg, "C stack overflow"))
|
||||
print("final count: ", count)
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
print("nesting of resuming yielded coroutines")
|
||||
local count = 0
|
||||
@@ -103,6 +129,20 @@ do
|
||||
end
|
||||
|
||||
|
||||
do -- bug in 5.4.2
|
||||
print("nesting coroutines running after recoverable errors")
|
||||
local count = 0
|
||||
local function foo()
|
||||
count = count + 1
|
||||
pcall(1) -- create an error
|
||||
-- running now inside 'precover' ("protected recover")
|
||||
coroutine.wrap(foo)() -- call another coroutine
|
||||
end
|
||||
checkerror("C stack overflow", foo)
|
||||
print("final count: ", count)
|
||||
end
|
||||
|
||||
|
||||
if T then
|
||||
print("testing stack recovery")
|
||||
local N = 0 -- trace number of calls
|
||||
|
||||
117
testes/db.lua
117
testes/db.lua
@@ -1,5 +1,5 @@
|
||||
-- $Id: testes/db.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
-- testing debug library
|
||||
|
||||
@@ -16,7 +16,7 @@ end
|
||||
assert(not debug.gethook())
|
||||
|
||||
local testline = 19 -- line where 'test' is defined
|
||||
function test (s, l, p) -- this must be line 19
|
||||
local function test (s, l, p) -- this must be line 19
|
||||
collectgarbage() -- avoid gc during trace
|
||||
local function f (event, line)
|
||||
assert(event == 'line')
|
||||
@@ -49,8 +49,17 @@ do
|
||||
end
|
||||
|
||||
|
||||
-- bug in 5.4.4-5.4.6: activelines in vararg functions
|
||||
-- without debug information
|
||||
do
|
||||
local func = load(string.dump(load("print(10)"), true))
|
||||
local actl = debug.getinfo(func, "L").activelines
|
||||
assert(#actl == 0) -- no line info
|
||||
end
|
||||
|
||||
|
||||
-- test file and string names truncation
|
||||
a = "function f () end"
|
||||
local 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))
|
||||
@@ -72,7 +81,8 @@ 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;
|
||||
_G.a = nil; _G.f = nil;
|
||||
_G[string.rep("p", 400)] = nil
|
||||
|
||||
|
||||
repeat
|
||||
@@ -118,7 +128,8 @@ then
|
||||
else
|
||||
a=2
|
||||
end
|
||||
]], {2,3,4,7})
|
||||
]], {2,4,7})
|
||||
|
||||
|
||||
test([[
|
||||
local function foo()
|
||||
@@ -128,6 +139,7 @@ A = 1
|
||||
A = 2
|
||||
A = 3
|
||||
]], {2, 3, 2, 4, 5, 6})
|
||||
_G.A = nil
|
||||
|
||||
|
||||
test([[--
|
||||
@@ -175,6 +187,8 @@ end
|
||||
|
||||
test([[for i=1,4 do a=1 end]], {1,1,1,1})
|
||||
|
||||
_G.a = nil
|
||||
|
||||
|
||||
do -- testing line info/trace with large gaps in source
|
||||
|
||||
@@ -194,6 +208,50 @@ do -- testing line info/trace with large gaps in source
|
||||
end
|
||||
end
|
||||
end
|
||||
_G.a = nil
|
||||
|
||||
|
||||
do -- testing active lines
|
||||
local function checkactivelines (f, lines)
|
||||
local t = debug.getinfo(f, "SL")
|
||||
for _, l in pairs(lines) do
|
||||
l = l + t.linedefined
|
||||
assert(t.activelines[l])
|
||||
t.activelines[l] = undef
|
||||
end
|
||||
assert(next(t.activelines) == nil) -- no extra lines
|
||||
end
|
||||
|
||||
checkactivelines(function (...) -- vararg function
|
||||
-- 1st line is empty
|
||||
-- 2nd line is empty
|
||||
-- 3th line is empty
|
||||
local a = 20
|
||||
-- 5th line is empty
|
||||
local b = 30
|
||||
-- 7th line is empty
|
||||
end, {4, 6, 8})
|
||||
|
||||
checkactivelines(function (a)
|
||||
-- 1st line is empty
|
||||
-- 2nd line is empty
|
||||
local a = 20
|
||||
local b = 30
|
||||
-- 5th line is empty
|
||||
end, {3, 4, 6})
|
||||
|
||||
checkactivelines(function (a, b, ...) end, {0})
|
||||
|
||||
checkactivelines(function (a, b)
|
||||
end, {1})
|
||||
|
||||
for _, n in pairs{0, 1, 2, 10, 50, 100, 1000, 10000} do
|
||||
checkactivelines(
|
||||
load(string.format("%s return 1", string.rep("\n", n))),
|
||||
{n + 1})
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
print'+'
|
||||
|
||||
@@ -244,7 +302,6 @@ 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
|
||||
|
||||
|
||||
|
||||
@@ -264,13 +321,14 @@ do -- test hook presence in debug info
|
||||
debug.sethook()
|
||||
assert(count == 4)
|
||||
end
|
||||
_ENV.a = nil
|
||||
|
||||
|
||||
-- hook table has weak keys
|
||||
assert(getmetatable(debug.getregistry()._HOOKKEY).__mode == 'k')
|
||||
|
||||
|
||||
a = {}; L = nil
|
||||
a = {}; local L = nil
|
||||
local glob = 1
|
||||
local oldglob = glob
|
||||
debug.sethook(function (e,l)
|
||||
@@ -291,12 +349,15 @@ end, "crl")
|
||||
|
||||
|
||||
function f(a,b)
|
||||
-- declare some globals to check that they don't interfere with 'getlocal'
|
||||
global collectgarbage
|
||||
collectgarbage()
|
||||
local _, x = debug.getlocal(1, 1)
|
||||
global assert, g, string
|
||||
local _, y = debug.getlocal(1, 2)
|
||||
assert(x == a and y == b)
|
||||
assert(debug.setlocal(2, 3, "pera") == "AA".."AA")
|
||||
assert(debug.setlocal(2, 4, "maçã") == "B")
|
||||
assert(debug.setlocal(2, 4, "manga") == "B")
|
||||
x = debug.getinfo(2)
|
||||
assert(x.func == g and x.what == "Lua" and x.name == 'g' and
|
||||
x.nups == 2 and string.find(x.source, "^@.*db%.lua$"))
|
||||
@@ -311,7 +372,7 @@ function foo()
|
||||
end; foo() -- set L
|
||||
-- check line counting inside strings and empty lines
|
||||
|
||||
_ = 'alo\
|
||||
local _ = 'alo\
|
||||
alo' .. [[
|
||||
|
||||
]]
|
||||
@@ -324,11 +385,13 @@ function g (...)
|
||||
local arg = {...}
|
||||
do local a,b,c; a=math.sin(40); end
|
||||
local feijao
|
||||
local AAAA,B = "xuxu", "mamão"
|
||||
local AAAA,B = "xuxu", "abacate"
|
||||
f(AAAA,B)
|
||||
assert(AAAA == "pera" and B == "maçã")
|
||||
assert(AAAA == "pera" and B == "manga")
|
||||
do
|
||||
global *
|
||||
local B = 13
|
||||
global<const> assert
|
||||
local x,y = debug.getlocal(1,5)
|
||||
assert(x == 'B' and y == 13)
|
||||
end
|
||||
@@ -360,6 +423,7 @@ function g(a,b) return (a+1) + f() end
|
||||
|
||||
assert(g(0,0) == 30)
|
||||
|
||||
_G.f, _G.g = nil
|
||||
|
||||
debug.sethook(nil);
|
||||
assert(not debug.gethook())
|
||||
@@ -372,7 +436,7 @@ do
|
||||
assert(a == nil and not b)
|
||||
end
|
||||
|
||||
-- testing iteraction between multiple values x hooks
|
||||
-- testing interaction between multiple values x hooks
|
||||
do
|
||||
local function f(...) return 3, ... end
|
||||
local count = 0
|
||||
@@ -403,7 +467,7 @@ local function collectlocals (level)
|
||||
end
|
||||
|
||||
|
||||
X = nil
|
||||
local X = nil
|
||||
a = {}
|
||||
function a:f (a, b, ...) local arg = {...}; local c = 13 end
|
||||
debug.sethook(function (e)
|
||||
@@ -426,6 +490,7 @@ 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())
|
||||
_G.XX = nil
|
||||
|
||||
|
||||
-- testing access to local variables in return hook (bug in 5.2)
|
||||
@@ -527,7 +592,7 @@ 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)
|
||||
-- upvalues of C functions are always named "" (the empty string)
|
||||
assert(debug.getupvalue(string.gmatch("x", "x"), 1) == "")
|
||||
|
||||
|
||||
@@ -550,6 +615,7 @@ end
|
||||
|
||||
debug.sethook()
|
||||
|
||||
local g, g1
|
||||
|
||||
-- tests for tail calls
|
||||
local function f (x)
|
||||
@@ -563,6 +629,9 @@ local function f (x)
|
||||
end
|
||||
end
|
||||
|
||||
assert(debug.getinfo(print, 't').istailcall == false)
|
||||
assert(debug.getinfo(print, 't').extraargs == 0)
|
||||
|
||||
function g(x) return f(x) end
|
||||
|
||||
function g1(x) g(x) end
|
||||
@@ -595,7 +664,7 @@ h(false)
|
||||
debug.sethook()
|
||||
assert(b == 2) -- two tail calls
|
||||
|
||||
lim = _soft and 3000 or 30000
|
||||
local lim = _soft and 3000 or 30000
|
||||
local function foo (x)
|
||||
if x==0 then
|
||||
assert(debug.getinfo(2).what == "main")
|
||||
@@ -637,7 +706,7 @@ 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("hi", 0), "'traceback'"))
|
||||
assert(string.find(debug.traceback(), "^stack traceback:\n"))
|
||||
|
||||
do -- C-function names in traceback
|
||||
@@ -765,7 +834,7 @@ end
|
||||
|
||||
co = coroutine.create(function (x) f(x) end)
|
||||
a, b = coroutine.resume(co, 3)
|
||||
t = {"'coroutine.yield'", "'f'", "in function <"}
|
||||
t = {"'yield'", "'f'", "in function <"}
|
||||
while coroutine.status(co) == "suspended" do
|
||||
checktraceback(co, t)
|
||||
a, b = coroutine.resume(co)
|
||||
@@ -775,7 +844,7 @@ t[1] = "'error'"
|
||||
checktraceback(co, t)
|
||||
|
||||
|
||||
-- test acessing line numbers of a coroutine from a resume inside
|
||||
-- test accessing 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)
|
||||
@@ -844,7 +913,7 @@ do -- testing debug info for finalizers
|
||||
|
||||
-- create a piece of garbage with a finalizer
|
||||
setmetatable({}, {__gc = function ()
|
||||
local t = debug.getinfo(2) -- get callee information
|
||||
local t = debug.getinfo(1) -- get function information
|
||||
assert(t.namewhat == "metamethod")
|
||||
name = t.name
|
||||
end})
|
||||
@@ -876,7 +945,7 @@ do
|
||||
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, "%.%.%.")
|
||||
local brk = string.find(rest, "%.%.%.\t%(skip")
|
||||
if brk then -- does message have '...'?
|
||||
local rest1 = string.sub(rest, 1, brk)
|
||||
local rest2 = string.sub(rest, brk, #rest)
|
||||
@@ -897,14 +966,14 @@ end
|
||||
|
||||
|
||||
print("testing debug functions on chunk without debug info")
|
||||
prog = [[-- program to be loaded without debug information (strip)
|
||||
local prog = [[-- program to be loaded without debug information (strip)
|
||||
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
|
||||
assert(n == "(temporary)" and v == debug) -- unknown name but known value
|
||||
n, v = debug.getlocal(1, 2)
|
||||
assert(n == "(temporary)" and v == 12) -- unkown name but known value
|
||||
assert(n == "(temporary)" and v == 12) -- unknown name but known value
|
||||
|
||||
-- a function with an upvalue
|
||||
local f = function () local x; return a end
|
||||
@@ -954,7 +1023,7 @@ do -- bug in 5.4.0: line hooks in stripped code
|
||||
line = l
|
||||
end, "l")
|
||||
assert(s() == 2); debug.sethook(nil)
|
||||
assert(line == nil) -- hook called withoug debug info for 1st instruction
|
||||
assert(line == nil) -- hook called without debug info for 1st instruction
|
||||
end
|
||||
|
||||
do -- tests for 'source' in binary dumps
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- $Id: testes/errors.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
print("testing errors")
|
||||
|
||||
@@ -26,7 +26,7 @@ end
|
||||
|
||||
local function checkmessage (prog, msg, debug)
|
||||
local m = doit(prog)
|
||||
if debug then print(m) end
|
||||
if debug then print(m, msg) end
|
||||
assert(string.find(m, msg, 1, true))
|
||||
end
|
||||
|
||||
@@ -45,8 +45,8 @@ 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 nil error message
|
||||
assert(doit("error()") == "<no error object>")
|
||||
|
||||
|
||||
-- test common errors/errors that crashed in the past
|
||||
@@ -91,7 +91,7 @@ end
|
||||
|
||||
if not T then
|
||||
(Message or print)
|
||||
('\n >>> testC not active: skipping memory message test <<<\n')
|
||||
('\n >>> testC not active: skipping tests for messages in C <<<\n')
|
||||
else
|
||||
print "testing memory error message"
|
||||
local a = {}
|
||||
@@ -104,6 +104,44 @@ else
|
||||
end)
|
||||
T.totalmem(0)
|
||||
assert(not st and msg == "not enough" .. " memory")
|
||||
|
||||
-- stack space for luaL_traceback (bug in 5.4.6)
|
||||
local res = T.testC[[
|
||||
# push 16 elements on the stack
|
||||
pushnum 1; pushnum 1; pushnum 1; pushnum 1; pushnum 1;
|
||||
pushnum 1; pushnum 1; pushnum 1; pushnum 1; pushnum 1;
|
||||
pushnum 1; pushnum 1; pushnum 1; pushnum 1; pushnum 1;
|
||||
pushnum 1;
|
||||
# traceback should work with 4 remaining slots
|
||||
traceback xuxu 1;
|
||||
return 1
|
||||
]]
|
||||
assert(string.find(res, "xuxu.-main chunk"))
|
||||
|
||||
do -- tests for error messages about extra arguments from __call
|
||||
local function createobj (n)
|
||||
-- function that raises an error on its n-th argument
|
||||
local code = string.format("argerror %d 'msg'", n)
|
||||
local func = T.makeCfunc(code)
|
||||
-- create a chain of 2 __call objects
|
||||
local M = setmetatable({}, {__call = func})
|
||||
M = setmetatable({}, {__call = M})
|
||||
-- put it as a method for a new object
|
||||
return {foo = M}
|
||||
end
|
||||
|
||||
_G.a = createobj(1) -- error in first (extra) argument
|
||||
checkmessage("a:foo()", "bad extra argument #1")
|
||||
|
||||
_G.a = createobj(2) -- error in second (extra) argument
|
||||
checkmessage("a:foo()", "bad extra argument #2")
|
||||
|
||||
_G.a = createobj(3) -- error in self (after two extra arguments)
|
||||
checkmessage("a:foo()", "bad self")
|
||||
|
||||
_G.a = createobj(4) -- error in first regular argument (after self)
|
||||
checkmessage("a:foo()", "bad argument #1")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -114,12 +152,20 @@ 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("aaa=1; bbbb=2; aaa=math.sin(3)+bbbb(3)", "global 'bbbb'")
|
||||
checkmessage("aaa={}; do local aaa=1 end aaa: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")
|
||||
assert(not string.find(doit"aaa={13}; local bbbb=1; aaa[bbbb](3)", "'bbbb'"))
|
||||
checkmessage("aaa={13}; local bbbb=1; aaa[bbbb](3)", "number")
|
||||
checkmessage("aaa=(1)..{}", "a table value")
|
||||
|
||||
-- bug in 5.4.6
|
||||
checkmessage("a = {_ENV = {}}; print(a._ENV.x + 1)", "field 'x'")
|
||||
|
||||
-- a similar bug, since 5.4.0
|
||||
checkmessage("print(('_ENV').x + 1)", "field 'x'")
|
||||
|
||||
_G.aaa, _G.bbbb = nil
|
||||
|
||||
-- calls
|
||||
checkmessage("local a; a(13)", "local 'a'")
|
||||
@@ -134,12 +180,13 @@ checkmessage([[
|
||||
|
||||
-- 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("aaa={}; do local aaa=1 end; return aaa:bbbb(3)", "method 'bbbb'")
|
||||
|
||||
checkmessage("a = #print", "length of a function value")
|
||||
checkmessage("a = #3", "length of a number value")
|
||||
checkmessage("aaa = #print", "length of a function value")
|
||||
checkmessage("aaa = #3", "length of a number value")
|
||||
|
||||
_G.aaa = nil
|
||||
|
||||
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'")
|
||||
@@ -152,15 +199,16 @@ 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("BB=1; local aaa={}; x=aaa+BB", "local 'aaa'")
|
||||
checkmessage("aaa={}; x=3.3/aaa", "global 'aaa'")
|
||||
checkmessage("aaa=2; b=nil;x=aaa*b", "global 'b'")
|
||||
checkmessage("aaa=2; BB=nil;x=aaa*BB", "global 'BB'")
|
||||
checkmessage("aaa={}; x=-aaa", "global 'aaa'")
|
||||
|
||||
-- short circuit
|
||||
checkmessage("a=1; local a,bbbb=2,3; a = math.sin(1) and bbbb(3)",
|
||||
checkmessage("aaa=1; local aaa,bbbb=2,3; aaa = 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("aaa=1; local aaa,bbbb=2,3; aaa = bbbb(1) or aaa(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'"))
|
||||
@@ -187,8 +235,8 @@ 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'")
|
||||
checkmessage("aaa = 24 // 0", "divide by zero")
|
||||
checkmessage("aaa = 1 % 0", "'n%0'")
|
||||
|
||||
|
||||
-- type error for an object which is neither in an upvalue nor a register.
|
||||
@@ -228,6 +276,22 @@ do -- named objects (field '__name')
|
||||
checkmessage("return {} < XX", "table with My Type")
|
||||
checkmessage("return XX < io.stdin", "My Type with FILE*")
|
||||
_G.XX = nil
|
||||
|
||||
if T then -- extra tests for 'luaL_tolstring'
|
||||
-- bug in 5.4.3; 'luaL_tolstring' with negative indices
|
||||
local x = setmetatable({}, {__name="TABLE"})
|
||||
assert(T.testC("Ltolstring -1; return 1", x) == tostring(x))
|
||||
|
||||
local a, b = T.testC("pushint 10; Ltolstring -2; return 2", x)
|
||||
assert(a == 10 and b == tostring(x))
|
||||
|
||||
setmetatable(x, {__tostring=function (o)
|
||||
assert(o == x)
|
||||
return "ABC"
|
||||
end})
|
||||
local a, b, c = T.testC("pushint 10; Ltolstring -2; return 3", x)
|
||||
assert(a == x and b == 10 and c == "ABC")
|
||||
end
|
||||
end
|
||||
|
||||
-- global functions
|
||||
@@ -239,28 +303,29 @@ do
|
||||
local f = function (a) return a + 1 end
|
||||
f = assert(load(string.dump(f, true)))
|
||||
assert(f(3) == 4)
|
||||
checkerr("^%?:%-1:", f, {})
|
||||
checkerr("^%?:%?:", 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)
|
||||
checkerr("^%?:%?:.*table value", f)
|
||||
end
|
||||
|
||||
|
||||
-- tests for field accesses after RK limit
|
||||
local t = {}
|
||||
for i = 1, 1000 do
|
||||
t[i] = "a = x" .. i
|
||||
t[i] = "aaa = 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(s.."; aaa = bbb + 1", "global 'bbb'")
|
||||
checkmessage("local _ENV=_ENV;"..s.."; aaa = bbb + 1", "global 'bbb'")
|
||||
checkmessage(s.."; local t = {}; aaa = t.bbb + 1", "field 'bbb'")
|
||||
-- cannot use 'self' opcode
|
||||
checkmessage(s.."; local t = {}; t:bbb()", "field 'bbb'")
|
||||
|
||||
checkmessage([[aaa=9
|
||||
repeat until 3==3
|
||||
@@ -289,7 +354,7 @@ end]], "global 'insert'")
|
||||
|
||||
checkmessage([[ -- tail call
|
||||
return math.sin("a")
|
||||
]], "'sin'")
|
||||
]], "sin")
|
||||
|
||||
checkmessage([[collectgarbage("nooption")]], "invalid option")
|
||||
|
||||
@@ -308,14 +373,17 @@ main()
|
||||
]], "global 'NoSuchName'")
|
||||
print'+'
|
||||
|
||||
a = {}; setmetatable(a, {__index = string})
|
||||
checkmessage("a:sub()", "bad self")
|
||||
aaa = {}; setmetatable(aaa, {__index = string})
|
||||
checkmessage("aaa: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'")
|
||||
|
||||
_G.aaa = nil
|
||||
|
||||
|
||||
-- tests for errors in coroutines
|
||||
|
||||
local function f (n)
|
||||
@@ -333,7 +401,7 @@ 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 idsize = 60 - 1
|
||||
local function checksize (source)
|
||||
-- syntax error
|
||||
local _, msg = load("x", source)
|
||||
@@ -369,19 +437,19 @@ lineerror("a\n=\n-\n\nprint\n;", 3)
|
||||
|
||||
lineerror([[
|
||||
a
|
||||
(
|
||||
( -- <<
|
||||
23)
|
||||
]], 1)
|
||||
]], 2)
|
||||
|
||||
lineerror([[
|
||||
local a = {x = 13}
|
||||
a
|
||||
.
|
||||
x
|
||||
(
|
||||
( -- <<
|
||||
23
|
||||
)
|
||||
]], 2)
|
||||
]], 5)
|
||||
|
||||
lineerror([[
|
||||
local a = {x = 13}
|
||||
@@ -395,13 +463,14 @@ x
|
||||
|
||||
local p = [[
|
||||
function g() f() end
|
||||
function f(x) error('a', X) end
|
||||
function f(x) error('a', XX) end
|
||||
g()
|
||||
]]
|
||||
X=3;lineerror((p), 3)
|
||||
X=0;lineerror((p), false)
|
||||
X=1;lineerror((p), 2)
|
||||
X=2;lineerror((p), 1)
|
||||
XX=3;lineerror((p), 3)
|
||||
XX=0;lineerror((p), false)
|
||||
XX=1;lineerror((p), 2)
|
||||
XX=2;lineerror((p), 1)
|
||||
_G.XX, _G.g, _G.f = nil
|
||||
|
||||
|
||||
lineerror([[
|
||||
@@ -420,6 +489,22 @@ if not b then
|
||||
end
|
||||
end]], 5)
|
||||
|
||||
lineerror([[
|
||||
_ENV = 1
|
||||
global function foo ()
|
||||
local a = 10
|
||||
return a
|
||||
end
|
||||
]], 2)
|
||||
|
||||
|
||||
-- bug in 5.4.0
|
||||
lineerror([[
|
||||
local a = 0
|
||||
local b = 1
|
||||
local c = b % a
|
||||
]], 3)
|
||||
|
||||
do
|
||||
-- Force a negative estimate for base line. Error in instruction 2
|
||||
-- (after VARARGPREP, GETGLOBAL), with first absolute line information
|
||||
@@ -430,14 +515,14 @@ end
|
||||
|
||||
|
||||
if not _soft then
|
||||
-- several tests that exaust the Lua stack
|
||||
-- several tests that exhaust the Lua stack
|
||||
collectgarbage()
|
||||
print"testing stack overflow"
|
||||
C = 0
|
||||
local C = 0
|
||||
-- get line where stack overflow will happen
|
||||
local l = debug.getinfo(1, "l").currentline + 1
|
||||
local function auxy () C=C+1; auxy() end -- produce a stack overflow
|
||||
function y ()
|
||||
function YY ()
|
||||
collectgarbage("stop") -- avoid running finalizers without stack space
|
||||
auxy()
|
||||
collectgarbage("restart")
|
||||
@@ -449,9 +534,11 @@ if not _soft then
|
||||
return (string.find(m, "stack overflow"))
|
||||
end
|
||||
-- repeated stack overflows (to check stack recovery)
|
||||
assert(checkstackmessage(doit('y()')))
|
||||
assert(checkstackmessage(doit('y()')))
|
||||
assert(checkstackmessage(doit('y()')))
|
||||
assert(checkstackmessage(doit('YY()')))
|
||||
assert(checkstackmessage(doit('YY()')))
|
||||
assert(checkstackmessage(doit('YY()')))
|
||||
|
||||
_G.YY = nil
|
||||
|
||||
|
||||
-- error lines in stack overflow
|
||||
@@ -479,7 +566,7 @@ if not _soft then
|
||||
|
||||
-- error in error handling
|
||||
local res, msg = xpcall(error, error)
|
||||
assert(not res and type(msg) == 'string')
|
||||
assert(not res and msg == 'error in error handling')
|
||||
print('+')
|
||||
|
||||
local function f (x)
|
||||
@@ -510,6 +597,27 @@ if not _soft then
|
||||
end
|
||||
|
||||
|
||||
do -- errors in error handle that not necessarily go forever
|
||||
local function err (n) -- function to be used as message handler
|
||||
-- generate an error unless n is zero, so that there is a limited
|
||||
-- loop of errors
|
||||
if type(n) ~= "number" then -- some other error?
|
||||
return n -- report it
|
||||
elseif n == 0 then
|
||||
return "END" -- that will be the final message
|
||||
else error(n - 1) -- does the loop
|
||||
end
|
||||
end
|
||||
|
||||
local res, msg = xpcall(error, err, 170)
|
||||
assert(not res and msg == "END")
|
||||
|
||||
-- too many levels
|
||||
local res, msg = xpcall(error, err, 300)
|
||||
assert(not res and msg == "C stack overflow")
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
-- non string messages
|
||||
local t = {}
|
||||
@@ -517,7 +625,7 @@ do
|
||||
assert(not res and msg == t)
|
||||
|
||||
res, msg = pcall(function () error(nil) end)
|
||||
assert(not res and msg == nil)
|
||||
assert(not res and msg == "<no error object>")
|
||||
|
||||
local function f() error{msg='x'} end
|
||||
res, msg = xpcall(f, function (r) return {msg=r.msg..'y'} end)
|
||||
@@ -537,7 +645,7 @@ do
|
||||
assert(not res and msg == t)
|
||||
|
||||
res, msg = pcall(assert, nil, nil)
|
||||
assert(not res and msg == nil)
|
||||
assert(not res and type(msg) == "string")
|
||||
|
||||
-- 'assert' without arguments
|
||||
res, msg = pcall(assert)
|
||||
@@ -545,7 +653,7 @@ do
|
||||
end
|
||||
|
||||
-- xpcall with arguments
|
||||
a, b, c = xpcall(string.find, error, "alo", "al")
|
||||
local 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)
|
||||
@@ -565,11 +673,12 @@ 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)
|
||||
doit('I = load("a=9+"); aaa=3')
|
||||
assert(_G.aaa==3 and not _G.I)
|
||||
_G.I,_G.aaa = nil
|
||||
print('+')
|
||||
|
||||
lim = 1000
|
||||
local lim = 1000
|
||||
if _soft then lim = 100 end
|
||||
for i=1,lim do
|
||||
doit('a = ')
|
||||
@@ -633,7 +742,7 @@ assert(c > 255 and string.find(b, "too many upvalues") and
|
||||
|
||||
-- local variables
|
||||
s = "\nfunction foo ()\n local "
|
||||
for j = 1,300 do
|
||||
for j = 1,200 do
|
||||
s = s.."a"..j..", "
|
||||
end
|
||||
s = s.."b\n"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- $Id: testes/events.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
print('testing metatables')
|
||||
|
||||
@@ -248,6 +248,15 @@ end
|
||||
test(Op(1), Op(2), Op(3))
|
||||
|
||||
|
||||
do -- test nil as false
|
||||
local x = setmetatable({12}, {__eq= function (a,b)
|
||||
return a[1] == b[1] or nil
|
||||
end})
|
||||
assert(not (x == {20}))
|
||||
assert(x == {12})
|
||||
end
|
||||
|
||||
|
||||
-- test `partial order'
|
||||
|
||||
local function rawSet(x)
|
||||
@@ -370,6 +379,17 @@ x = 0 .."a".."b"..c..d.."e".."f".."g"
|
||||
assert(x.val == "0abcdefg")
|
||||
|
||||
|
||||
do
|
||||
-- bug since 5.4.1 (test needs T)
|
||||
local mt = setmetatable({__newindex={}}, {__mode='v'})
|
||||
local t = setmetatable({}, mt)
|
||||
|
||||
if T then T.allocfailnext() end
|
||||
|
||||
-- seg. fault
|
||||
for i=1, 10 do t[i] = 1 end
|
||||
end
|
||||
|
||||
-- concat metamethod x numbers (bug in 5.1.1)
|
||||
c = {}
|
||||
local x
|
||||
@@ -420,6 +440,9 @@ assert(i == 3 and x[1] == 3 and x[3] == 5)
|
||||
|
||||
assert(_G.X == 20)
|
||||
|
||||
_G.X, _G.B = nil
|
||||
|
||||
|
||||
print'+'
|
||||
|
||||
local _g = _G
|
||||
@@ -469,7 +492,7 @@ 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
|
||||
local T, K, V = nil
|
||||
grandparent = {}
|
||||
grandparent.__newindex = function(t,k,v) T=t; K=k; V=v end
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
-- $Id: testes/files.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
global <const> *
|
||||
|
||||
local debug = require "debug"
|
||||
|
||||
@@ -74,6 +76,8 @@ io.input(io.stdin); io.output(io.stdout);
|
||||
|
||||
os.remove(file)
|
||||
assert(not loadfile(file))
|
||||
-- Lua code cannot use chunks with fixed buffers
|
||||
checkerr("invalid mode", load, "", "", "B")
|
||||
checkerr("", dofile, file)
|
||||
assert(not io.open(file))
|
||||
io.output(file)
|
||||
@@ -92,8 +96,8 @@ 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'))
|
||||
assert(io.write('"alo"', "{a}\n", "second line\n", "third line \n"))
|
||||
assert(io.write('Xfourth_line'))
|
||||
io.output(io.stdout)
|
||||
collectgarbage() -- file should be closed by GC
|
||||
assert(io.input() == io.stdin and rawequal(io.output(), io.stdout))
|
||||
@@ -300,14 +304,14 @@ do -- test error returns
|
||||
end
|
||||
checkerr("invalid format", io.read, "x")
|
||||
assert(io.read(0) == "") -- not eof
|
||||
assert(io.read(5, 'l') == '"álo"')
|
||||
assert(io.read(5, 'l') == '"alo"')
|
||||
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(1) == "X")
|
||||
assert(io.read(string.len"fourth_line") == "fourth_line")
|
||||
assert(io.input():seek("cur", -string.len"fourth_line"))
|
||||
assert(io.read() == "fourth_line")
|
||||
@@ -345,7 +349,7 @@ collectgarbage()
|
||||
|
||||
assert(io.write(' ' .. t .. ' '))
|
||||
assert(io.write(';', 'end of file\n'))
|
||||
f:flush(); io.flush()
|
||||
assert(f:flush()); assert(io.flush())
|
||||
f:close()
|
||||
print('+')
|
||||
|
||||
@@ -427,12 +431,12 @@ 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
|
||||
local stvar = 0 -- to-be-closed is 3th 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
|
||||
if stvar == 3 then return v end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -459,7 +463,24 @@ do -- testing closing file in line iteration
|
||||
end
|
||||
|
||||
|
||||
-- test for multipe arguments in 'lines'
|
||||
do print("testing flush")
|
||||
local f = io.output("/dev/null")
|
||||
assert(f:write("abcd")) -- write to buffer
|
||||
assert(f:flush()) -- write to device
|
||||
assert(f:write("abcd")) -- write to buffer
|
||||
assert(io.flush()) -- write to device
|
||||
assert(f:close())
|
||||
|
||||
local f = io.output("/dev/full")
|
||||
assert(f:write("abcd")) -- write to buffer
|
||||
assert(not f:flush()) -- cannot write to device
|
||||
assert(f:write("abcd")) -- write to buffer
|
||||
assert(not io.flush()) -- cannot write to device
|
||||
assert(f:close())
|
||||
end
|
||||
|
||||
|
||||
-- test for multiple 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)
|
||||
@@ -507,15 +528,17 @@ load((io.lines(file, 1)))()
|
||||
assert(_G.X == 4)
|
||||
load((io.lines(file, 3)))()
|
||||
assert(_G.X == 8)
|
||||
_G.X = nil
|
||||
|
||||
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)))
|
||||
assert(io.write(string.format("X2 = %q\n-- comment without ending EOS", x1)))
|
||||
io.close()
|
||||
assert(loadfile(file))()
|
||||
assert(x1 == x2)
|
||||
assert(x1 == _G.X2)
|
||||
_G.X2 = nil
|
||||
print('+')
|
||||
assert(os.remove(file))
|
||||
assert(not os.remove(file))
|
||||
@@ -692,6 +715,37 @@ do
|
||||
end
|
||||
|
||||
|
||||
if T and T.nonblock then
|
||||
print("testing failed write")
|
||||
|
||||
-- unable to write anything to /dev/full
|
||||
local f = io.open("/dev/full", "w")
|
||||
assert(f:setvbuf("no"))
|
||||
local _, _, err, count = f:write("abcd")
|
||||
assert(err > 0 and count == 0)
|
||||
assert(f:close())
|
||||
|
||||
-- receiver will read a "few" bytes (enough to empty a large buffer)
|
||||
local receiver = [[
|
||||
lua -e 'assert(io.stdin:setvbuf("no")); assert(#io.read(1e4) == 1e4)' ]]
|
||||
|
||||
local f = io.popen(receiver, "w")
|
||||
assert(f:setvbuf("no"))
|
||||
T.nonblock(f)
|
||||
|
||||
-- able to write a few bytes
|
||||
assert(f:write(string.rep("a", 1e2)))
|
||||
|
||||
-- Unable to write more bytes than the pipe buffer supports.
|
||||
-- (In Linux, the pipe buffer size is 64K (2^16). Posix requires at
|
||||
-- least 512 bytes.)
|
||||
local _, _, err, count = f:write("abcd", string.rep("a", 2^17))
|
||||
assert(err > 0 and count >= 512 and count < 2^17)
|
||||
|
||||
assert(f:close())
|
||||
end
|
||||
|
||||
|
||||
if not _soft then
|
||||
print("testing large files (> BUFSIZ)")
|
||||
io.output(file)
|
||||
@@ -762,6 +816,7 @@ if not _port then
|
||||
assert((v[3] == nil and z > 0) or v[3] == z)
|
||||
end
|
||||
end
|
||||
print("(done)")
|
||||
end
|
||||
|
||||
|
||||
@@ -785,13 +840,13 @@ 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)
|
||||
global D; 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)
|
||||
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
|
||||
@@ -825,8 +880,17 @@ 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})
|
||||
|
||||
checkerr("field 'year' is out-of-bound", os.time,
|
||||
{year = -(1 << 31), month = 1, day = 1})
|
||||
|
||||
if math.maxinteger > 2^31 then -- larger lua_integer?
|
||||
checkerr("field 'year' is out-of-bound", os.time,
|
||||
{year = (1 << 31) + 1900, month = 1, day = 1})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if not _port then
|
||||
-- test Posix-specific modifiers
|
||||
assert(type(os.date("%Ex")) == 'string')
|
||||
|
||||
185
testes/gc.lua
185
testes/gc.lua
@@ -1,5 +1,5 @@
|
||||
-- $Id: testes/gc.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
print('testing incremental garbage collection')
|
||||
|
||||
@@ -27,27 +27,54 @@ end
|
||||
|
||||
-- test weird parameters to 'collectgarbage'
|
||||
do
|
||||
-- save original parameters
|
||||
local a = collectgarbage("setpause", 200)
|
||||
local b = collectgarbage("setstepmul", 200)
|
||||
collectgarbage("incremental")
|
||||
local opause = collectgarbage("param", "pause", 100)
|
||||
local ostepmul = collectgarbage("param", "stepmul", 100)
|
||||
assert(collectgarbage("param", "pause") == 100)
|
||||
assert(collectgarbage("param", "stepmul") == 100)
|
||||
local t = {0, 2, 10, 90, 500, 5000, 30000, 0x7ffffffe}
|
||||
for i = 1, #t do
|
||||
local p = t[i]
|
||||
collectgarbage("param", "pause", t[i])
|
||||
for j = 1, #t do
|
||||
local m = t[j]
|
||||
collectgarbage("setpause", p)
|
||||
collectgarbage("setstepmul", m)
|
||||
collectgarbage("step", 0)
|
||||
collectgarbage("step", 10000)
|
||||
collectgarbage("param", "stepmul", t[j])
|
||||
collectgarbage("step", t[j])
|
||||
end
|
||||
end
|
||||
-- restore original parameters
|
||||
collectgarbage("setpause", a)
|
||||
collectgarbage("setstepmul", b)
|
||||
collectgarbage("param", "pause", opause)
|
||||
collectgarbage("param", "stepmul", ostepmul)
|
||||
collectgarbage()
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- test the "size" of basic GC steps (whatever they mean...)
|
||||
--
|
||||
do print("steps")
|
||||
|
||||
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
|
||||
|
||||
|
||||
if not _port then
|
||||
collectgarbage"stop"
|
||||
assert(dosteps(10) < dosteps(2))
|
||||
collectgarbage"restart"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
_G["while"] = 234
|
||||
|
||||
|
||||
@@ -125,7 +152,7 @@ do
|
||||
end
|
||||
|
||||
a:test()
|
||||
|
||||
_G.temp = nil
|
||||
end
|
||||
|
||||
|
||||
@@ -134,7 +161,7 @@ do local f = function () end end
|
||||
|
||||
|
||||
print("functions with errors")
|
||||
prog = [[
|
||||
local prog = [[
|
||||
do
|
||||
a = 10;
|
||||
function foo(x,y)
|
||||
@@ -153,60 +180,24 @@ do
|
||||
end
|
||||
end
|
||||
end
|
||||
rawset(_G, "a", nil)
|
||||
_G.x = nil
|
||||
|
||||
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"))
|
||||
foo = nil
|
||||
print('long strings')
|
||||
local x = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||
assert(string.len(x)==80)
|
||||
local s = ''
|
||||
local k = math.min(300, (math.maxinteger // 80) // 2)
|
||||
for n = 1, k do s = s..x; local j=tostring(n) end
|
||||
assert(string.len(s) == k*80)
|
||||
s = string.sub(s, 1, 10000)
|
||||
local s, i = string.gsub(s, '(%d%d%d%d)', '')
|
||||
assert(i==10000 // 4)
|
||||
|
||||
assert(_G["while"] == 234)
|
||||
_G["while"] = nil
|
||||
end
|
||||
|
||||
|
||||
@@ -227,8 +218,8 @@ end
|
||||
|
||||
|
||||
print("clearing tables")
|
||||
lim = 15
|
||||
a = {}
|
||||
local lim = 15
|
||||
local a = {}
|
||||
-- fill a with `collectable' indices
|
||||
for i=1,lim do a[{}] = i end
|
||||
b = {}
|
||||
@@ -297,6 +288,21 @@ x,y,z=nil
|
||||
collectgarbage()
|
||||
assert(next(a) == string.rep('$', 11))
|
||||
|
||||
do -- invalid mode
|
||||
local a = setmetatable({}, {__mode = 34})
|
||||
collectgarbage()
|
||||
end
|
||||
|
||||
|
||||
if T then -- bug since 5.3: all-weak tables are not being revisited
|
||||
T.gcstate("propagate")
|
||||
local t = setmetatable({}, {__mode = "kv"})
|
||||
T.gcstate("enteratomic") -- 't' was visited
|
||||
setmetatable(t, {__mode = "kv"})
|
||||
T.gcstate("pause") -- its new metatable is not being visited
|
||||
assert(getmetatable(t).__mode == "kv")
|
||||
end
|
||||
|
||||
|
||||
-- 'bug' in 5.1
|
||||
a = {}
|
||||
@@ -371,7 +377,7 @@ if T then
|
||||
|
||||
warn("@on"); warn("@store")
|
||||
collectgarbage()
|
||||
assert(string.find(_WARN, "error in __gc metamethod"))
|
||||
assert(string.find(_WARN, "error in __gc"))
|
||||
assert(string.match(_WARN, "@(.-)@") == "expected"); _WARN = false
|
||||
for i = 8, 10 do assert(s[i]) end
|
||||
|
||||
@@ -455,12 +461,9 @@ do -- tests for string keys in weak tables
|
||||
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
|
||||
a[string.rep("b", 2^22)] = {} -- long string key -> collectable value
|
||||
a[{}] = 14 -- collectable key
|
||||
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
|
||||
@@ -471,7 +474,7 @@ do -- tests for string keys in weak tables
|
||||
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
|
||||
assert(collectgarbage("count") <= m + 1) -- everything collected
|
||||
end
|
||||
|
||||
|
||||
@@ -536,7 +539,7 @@ do
|
||||
local co = coroutine.create(f)
|
||||
assert(coroutine.resume(co, co))
|
||||
end
|
||||
-- Now, thread and closure are not reacheable any more.
|
||||
-- Now, thread and closure are not reachable any more.
|
||||
collectgarbage()
|
||||
assert(collected)
|
||||
collectgarbage("restart")
|
||||
@@ -546,12 +549,13 @@ end
|
||||
do
|
||||
collectgarbage()
|
||||
collectgarbage"stop"
|
||||
collectgarbage("step", 0) -- steps should not unblock the collector
|
||||
collectgarbage("step") -- 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"
|
||||
_ENV.a = nil
|
||||
end
|
||||
|
||||
|
||||
@@ -571,8 +575,8 @@ if T then -- tests for weird cases collecting upvalues
|
||||
-- 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")
|
||||
T.gcstate("enteratomic") -- ensure all objects are traversed
|
||||
assert(T.gcstate() == "enteratomic")
|
||||
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
|
||||
@@ -585,7 +589,7 @@ if T then -- tests for weird cases collecting upvalues
|
||||
collectgarbage()
|
||||
collectgarbage"stop"
|
||||
local a = {} -- avoid 'u' as first element in 'allgc'
|
||||
T.gcstate"atomic"
|
||||
T.gcstate"enteratomic"
|
||||
T.gcstate"sweepallgc"
|
||||
local x = {}
|
||||
assert(T.gccolor(u) == "black") -- userdata is "old" (black)
|
||||
@@ -611,6 +615,21 @@ if T then
|
||||
end
|
||||
|
||||
|
||||
if T then
|
||||
collectgarbage("stop")
|
||||
T.gcstate("pause")
|
||||
local sup = {x = 0}
|
||||
local a = setmetatable({}, {__newindex = sup})
|
||||
T.gcstate("enteratomic")
|
||||
assert(T.gccolor(sup) == "black")
|
||||
a.x = {} -- should not break the invariant
|
||||
assert(not (T.gccolor(sup) == "black" and T.gccolor(sup.x) == "white"))
|
||||
T.gcstate("pause") -- complete the GC cycle
|
||||
sup.x.y = 10
|
||||
collectgarbage("restart")
|
||||
end
|
||||
|
||||
|
||||
if T then
|
||||
print("emergency collections")
|
||||
collectgarbage()
|
||||
@@ -640,7 +659,7 @@ do
|
||||
assert(getmetatable(o) == tt)
|
||||
-- create new objects during GC
|
||||
local a = 'xuxu'..(10+3)..'joao', {}
|
||||
___Glob = o -- ressurrect object!
|
||||
___Glob = o -- resurrect object!
|
||||
setmetatable({}, tt) -- creates a new one with same metatable
|
||||
print(">>> closing state " .. "<<<\n")
|
||||
end
|
||||
@@ -676,11 +695,13 @@ end
|
||||
-- just to make sure
|
||||
assert(collectgarbage'isrunning')
|
||||
|
||||
do -- check that the collector is reentrant in incremental mode
|
||||
do -- check that the collector is not reentrant in incremental mode
|
||||
local res = true
|
||||
setmetatable({}, {__gc = function ()
|
||||
collectgarbage()
|
||||
res = collectgarbage()
|
||||
end})
|
||||
collectgarbage()
|
||||
assert(not res)
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- $Id: testes/gengc.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
print('testing generational garbage collection')
|
||||
|
||||
@@ -24,12 +24,12 @@ do
|
||||
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)
|
||||
collectgarbage("step")
|
||||
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)
|
||||
collectgarbage("step")
|
||||
assert(not T or (T.gcage(U) == "old" and T.gcage(U[1]) == "old1"))
|
||||
|
||||
-- data was not corrupted
|
||||
@@ -46,10 +46,10 @@ do
|
||||
assert(not T or T.gcage(old) == "old")
|
||||
setmetatable(old, {}) -- new table becomes OLD0 (barrier)
|
||||
assert(not T or T.gcage(getmetatable(old)) == "old0")
|
||||
collectgarbage("step", 0) -- new table becomes OLD1 and firstold1
|
||||
collectgarbage("step") -- new table becomes OLD1 and firstold1
|
||||
assert(not T or T.gcage(getmetatable(old)) == "old1")
|
||||
setmetatable(getmetatable(old), {__gc = foo}) -- get it out of allgc list
|
||||
collectgarbage("step", 0) -- should not seg. fault
|
||||
collectgarbage("step") -- should not seg. fault
|
||||
end
|
||||
|
||||
|
||||
@@ -65,18 +65,18 @@ do -- bug in 5.4.0
|
||||
A[1] = obj -- anchor object
|
||||
assert(not T or T.gcage(obj) == "old1")
|
||||
obj = nil -- remove it from the stack
|
||||
collectgarbage("step", 0) -- do a young collection
|
||||
collectgarbage("step") -- do a young collection
|
||||
print(getmetatable(A[1]).x) -- metatable was collected
|
||||
end
|
||||
|
||||
collectgarbage() -- make A old
|
||||
local obj = {} -- create a new object
|
||||
collectgarbage("step", 0) -- make it a survival
|
||||
collectgarbage("step") -- make it a survival
|
||||
assert(not T or T.gcage(obj) == "survival")
|
||||
setmetatable(obj, {__gc = gcf, x = "+"}) -- create its metatable
|
||||
assert(not T or T.gcage(getmetatable(obj)) == "new")
|
||||
obj = nil -- clear object
|
||||
collectgarbage("step", 0) -- will call obj's finalizer
|
||||
collectgarbage("step") -- will call obj's finalizer
|
||||
end
|
||||
|
||||
|
||||
@@ -94,13 +94,13 @@ do -- another bug in 5.4.0
|
||||
end
|
||||
)
|
||||
local _, f = coroutine.resume(co) -- create closure over 'x' in coroutine
|
||||
collectgarbage("step", 0) -- make upvalue a survival
|
||||
collectgarbage("step") -- make upvalue a survival
|
||||
old[1] = {"hello"} -- 'old' go to grayagain as 'touched1'
|
||||
coroutine.resume(co, {123}) -- its value will be new
|
||||
co = nil
|
||||
collectgarbage("step", 0) -- hit the barrier
|
||||
collectgarbage("step") -- hit the barrier
|
||||
assert(f() == 123 and old[1][1] == "hello")
|
||||
collectgarbage("step", 0) -- run the collector once more
|
||||
collectgarbage("step") -- run the collector once more
|
||||
-- make sure old[1] was not collected
|
||||
assert(f() == 123 and old[1][1] == "hello")
|
||||
end
|
||||
@@ -112,12 +112,12 @@ do -- bug introduced in commit 9cf3299fa
|
||||
assert(not T or T.gcage(t) == "old")
|
||||
t[1] = {10}
|
||||
assert(not T or (T.gcage(t) == "touched1" and T.gccolor(t) == "gray"))
|
||||
collectgarbage("step", 0) -- minor collection
|
||||
collectgarbage("step") -- minor collection
|
||||
assert(not T or (T.gcage(t) == "touched2" and T.gccolor(t) == "black"))
|
||||
collectgarbage("step", 0) -- minor collection
|
||||
collectgarbage("step") -- minor collection
|
||||
assert(not T or T.gcage(t) == "old") -- t should be black, but it was gray
|
||||
t[1] = {10} -- no barrier here, so t was still old
|
||||
collectgarbage("step", 0) -- minor collection
|
||||
collectgarbage("step") -- minor collection
|
||||
-- t, being old, is ignored by the collection, so it is not cleared
|
||||
assert(t[1] == nil) -- fails with the bug
|
||||
end
|
||||
@@ -144,13 +144,13 @@ do
|
||||
T.gcage(debug.getuservalue(U)) == "new")
|
||||
|
||||
-- both U and the table survive one more collection
|
||||
collectgarbage("step", 0)
|
||||
collectgarbage("step")
|
||||
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)
|
||||
collectgarbage("step")
|
||||
assert(T.gcage(U) == "old" and
|
||||
T.gcage(debug.getuservalue(U)) == "old1")
|
||||
|
||||
@@ -162,9 +162,19 @@ end
|
||||
assert(collectgarbage'isrunning')
|
||||
|
||||
|
||||
do print"testing stop-the-world collection"
|
||||
local step = collectgarbage("param", "stepsize", 0);
|
||||
collectgarbage("incremental")
|
||||
assert(collectgarbage("param", "stepsize") == 0)
|
||||
|
||||
-- just to make sure
|
||||
assert(collectgarbage'isrunning')
|
||||
-- each step does a complete cycle
|
||||
assert(collectgarbage("step"))
|
||||
assert(collectgarbage("step"))
|
||||
|
||||
-- back to default value
|
||||
collectgarbage("param", "stepsize", step);
|
||||
assert(collectgarbage("param", "stepsize") == step)
|
||||
end
|
||||
|
||||
collectgarbage(oldmode)
|
||||
|
||||
|
||||
180
testes/goto.lua
180
testes/goto.lua
@@ -1,5 +1,11 @@
|
||||
-- $Id: testes/goto.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
global<const> require
|
||||
global<const> print, load, assert, string, setmetatable
|
||||
global<const> collectgarbage, error
|
||||
|
||||
print("testing goto and global declarations")
|
||||
|
||||
collectgarbage()
|
||||
|
||||
@@ -17,15 +23,18 @@ 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
|
||||
-- jumping over variable declaration
|
||||
errmsg([[ goto l1; local aa ::l1:: ::l2:: print(3) ]], "scope of 'aa'")
|
||||
|
||||
errmsg([[ goto l2; global *; ::l1:: ::l2:: print(3) ]], "scope of '*'")
|
||||
|
||||
errmsg([[
|
||||
do local bb, cc; goto l1; end
|
||||
local aa
|
||||
::l1:: print(3)
|
||||
]], "local 'aa'")
|
||||
]], "scope of 'aa'")
|
||||
|
||||
|
||||
-- jumping into a block
|
||||
errmsg([[ do ::l1:: end goto l1 ]], "label 'l1'")
|
||||
@@ -38,7 +47,7 @@ errmsg([[
|
||||
local xuxu = 10
|
||||
::cont::
|
||||
until xuxu < x
|
||||
]], "local 'xuxu'")
|
||||
]], "scope of 'xuxu'")
|
||||
|
||||
-- simple gotos
|
||||
local x
|
||||
@@ -250,22 +259,157 @@ 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
|
||||
do -- test goto's around to-be-closed variable
|
||||
|
||||
::L2:: goto L3
|
||||
global *
|
||||
|
||||
::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'
|
||||
-- set 'var' and return an object that will reset 'var' when
|
||||
-- it goes out of scope
|
||||
local function newobj (var)
|
||||
_ENV[var] = true
|
||||
return setmetatable({}, {__close = function ()
|
||||
_ENV[var] = nil
|
||||
end})
|
||||
end
|
||||
|
||||
::L3:: assert(X == true) -- checks that 'a' was correctly closed
|
||||
end
|
||||
--------------------------------------------------------------------------------
|
||||
goto L1
|
||||
|
||||
::L4:: assert(not varX); goto L5 -- varX dead here
|
||||
|
||||
::L1::
|
||||
local varX <close> = newobj("X")
|
||||
assert(varX); goto L2 -- varX alive here
|
||||
|
||||
::L3::
|
||||
assert(varX); goto L4 -- varX alive here
|
||||
|
||||
::L2:: assert(varX); goto L3 -- varX alive here
|
||||
|
||||
::L5:: -- return
|
||||
end
|
||||
|
||||
|
||||
|
||||
foo()
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
local function checkerr (code, err)
|
||||
local st, msg = load(code)
|
||||
assert(not st and string.find(msg, err))
|
||||
end
|
||||
|
||||
do
|
||||
global T<const>
|
||||
|
||||
-- globals must be declared, after a global declaration
|
||||
checkerr("global none; X = 1", "variable 'X'")
|
||||
checkerr("global none; function XX() end", "variable 'XX'")
|
||||
|
||||
-- global variables cannot be to-be-closed
|
||||
checkerr("global X<close>", "cannot be")
|
||||
checkerr("global <close> *", "cannot be")
|
||||
|
||||
do
|
||||
local X = 10
|
||||
do global X; X = 20 end
|
||||
assert(X == 10) -- local X
|
||||
end
|
||||
assert(_ENV.X == 20) -- global X
|
||||
|
||||
-- '_ENV' cannot be global
|
||||
checkerr("global _ENV, a; a = 10", "variable 'a'")
|
||||
|
||||
-- global declarations inside functions
|
||||
checkerr([[
|
||||
global none
|
||||
local function foo () XXX = 1 end --< ERROR]], "variable 'XXX'")
|
||||
|
||||
if not T then -- when not in "test mode", "global" isn't reserved
|
||||
assert(load("global = 1; return global")() == 1)
|
||||
print " ('global' is not a reserved word)"
|
||||
else
|
||||
-- "global" reserved, cannot be used as a variable
|
||||
assert(not load("global = 1; return global"))
|
||||
end
|
||||
|
||||
local foo = 20
|
||||
do
|
||||
global function foo (x)
|
||||
if x == 0 then return 1 else return 2 * foo(x - 1) end
|
||||
end
|
||||
assert(foo == _ENV.foo and foo(4) == 16)
|
||||
end
|
||||
assert(_ENV.foo(4) == 16)
|
||||
assert(foo == 20) -- local one is in context here
|
||||
|
||||
do
|
||||
global foo;
|
||||
function foo (x) return end -- Ok after declaration
|
||||
end
|
||||
|
||||
checkerr([[
|
||||
global<const> foo;
|
||||
function foo (x) return end -- ERROR: foo is read-only
|
||||
]], "assign to const variable 'foo'")
|
||||
|
||||
checkerr([[
|
||||
global foo <const>;
|
||||
function foo (x) -- ERROR: foo is read-only
|
||||
return
|
||||
end
|
||||
]], "%:2%:") -- correct line in error message
|
||||
|
||||
checkerr([[
|
||||
global<const> *;
|
||||
print(X) -- Ok to use
|
||||
Y = 1 -- ERROR
|
||||
]], "assign to const variable 'Y'")
|
||||
|
||||
checkerr([[
|
||||
global *;
|
||||
Y = X -- Ok to use
|
||||
global<const> *;
|
||||
Y = 1 -- ERROR
|
||||
]], "assign to const variable 'Y'")
|
||||
|
||||
global *
|
||||
Y = 10
|
||||
assert(_ENV.Y == 10)
|
||||
global<const> *
|
||||
local x = Y
|
||||
global *
|
||||
Y = x + Y
|
||||
assert(_ENV.Y == 20)
|
||||
|
||||
end
|
||||
|
||||
|
||||
do -- Ok to declare hundreds of globals
|
||||
global table
|
||||
local code = {}
|
||||
for i = 1, 1000 do
|
||||
code[#code + 1] = ";global x" .. i
|
||||
end
|
||||
code[#code + 1] = "; return x990"
|
||||
code = table.concat(code)
|
||||
_ENV.x990 = 11
|
||||
assert(load(code)() == 11)
|
||||
_ENV.x990 = nil
|
||||
end
|
||||
|
||||
do -- mixing lots of global/local declarations
|
||||
global table
|
||||
local code = {}
|
||||
for i = 1, 200 do
|
||||
code[#code + 1] = ";global x" .. i
|
||||
code[#code + 1] = ";local y" .. i .. "=" .. (2*i)
|
||||
end
|
||||
code[#code + 1] = "; return x200 + y200"
|
||||
code = table.concat(code)
|
||||
_ENV.x200 = 11
|
||||
assert(assert(load(code))() == 2*200 + 11)
|
||||
_ENV.x200 = nil
|
||||
end
|
||||
|
||||
print'OK'
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- $Id: heavy.lua,v 1.7 2017/12/29 15:42:15 roberto Exp $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- $Id: testes/heavy.lua,v $
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
local function teststring ()
|
||||
print("creating a string too long")
|
||||
|
||||
@@ -5,23 +5,23 @@ LUA_DIR = ../../
|
||||
CC = gcc
|
||||
|
||||
# compilation should generate Dynamic-Link Libraries
|
||||
CFLAGS = -Wall -std=gnu99 -O2 -I$(LUA_DIR) -fPIC -shared
|
||||
CFLAGS = -Wall -std=c99 -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
|
||||
lib1.so: lib1.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/lua.h
|
||||
$(CC) $(CFLAGS) -o lib1.so lib1.c
|
||||
|
||||
lib11.so: lib11.c $(LUA_DIR)/luaconf.h
|
||||
lib11.so: lib11.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/lua.h
|
||||
$(CC) $(CFLAGS) -o lib11.so lib11.c
|
||||
|
||||
lib2.so: lib2.c $(LUA_DIR)/luaconf.h
|
||||
lib2.so: lib2.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/lua.h
|
||||
$(CC) $(CFLAGS) -o lib2.so lib2.c
|
||||
|
||||
lib21.so: lib21.c $(LUA_DIR)/luaconf.h
|
||||
lib21.so: lib21.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/lua.h
|
||||
$(CC) $(CFLAGS) -o lib21.so lib21.c
|
||||
|
||||
lib2-v2.so: lib21.c $(LUA_DIR)/luaconf.h
|
||||
lib2-v2.so: lib21.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/lua.h
|
||||
$(CC) $(CFLAGS) -o lib2-v2.so lib22.c
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
-- $Id: testes/literals.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
print('testing scanner')
|
||||
|
||||
global <const> *
|
||||
|
||||
local debug = require "debug"
|
||||
|
||||
|
||||
@@ -10,6 +12,7 @@ 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)
|
||||
_G.x = nil
|
||||
|
||||
-- escape sequences
|
||||
assert('\n\"\'\\' == [[
|
||||
@@ -129,16 +132,16 @@ end
|
||||
|
||||
-- long variable names
|
||||
|
||||
var1 = string.rep('a', 15000) .. '1'
|
||||
var2 = string.rep('a', 15000) .. '2'
|
||||
prog = string.format([[
|
||||
local var1 = string.rep('a', 15000) .. '1'
|
||||
local var2 = string.rep('a', 15000) .. '2'
|
||||
local 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
|
||||
_G[var1], _G[var2] = nil
|
||||
print('+')
|
||||
|
||||
-- escapes --
|
||||
@@ -150,13 +153,13 @@ assert([[
|
||||
$debug]] == "\n $debug")
|
||||
assert([[ [ ]] ~= [[ ] ]])
|
||||
-- long strings --
|
||||
b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789"
|
||||
local b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789"
|
||||
assert(string.len(b) == 960)
|
||||
prog = [=[
|
||||
print('+')
|
||||
|
||||
a1 = [["this is a 'string' with several 'quotes'"]]
|
||||
a2 = "'quotes'"
|
||||
local a1 = [["this is a 'string' with several 'quotes'"]]
|
||||
local a2 = "'quotes'"
|
||||
|
||||
assert(string.find(a1, a2) == 34)
|
||||
print('+')
|
||||
@@ -164,12 +167,13 @@ print('+')
|
||||
a1 = [==[temp = [[an arbitrary value]]; ]==]
|
||||
assert(load(a1))()
|
||||
assert(temp == 'an arbitrary value')
|
||||
_G.temp = nil
|
||||
-- long strings --
|
||||
b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789"
|
||||
local b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789"
|
||||
assert(string.len(b) == 960)
|
||||
print('+')
|
||||
|
||||
a = [[00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
local a = [[00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
@@ -199,19 +203,41 @@ x = 1
|
||||
]=]
|
||||
|
||||
print('+')
|
||||
x = nil
|
||||
_G.x = nil
|
||||
dostring(prog)
|
||||
assert(x)
|
||||
_G.x = nil
|
||||
|
||||
prog = nil
|
||||
a = nil
|
||||
b = nil
|
||||
|
||||
|
||||
do -- reuse of long strings
|
||||
|
||||
-- get the address of a string
|
||||
local function getadd (s) return string.format("%p", s) end
|
||||
|
||||
local s1 <const> = "01234567890123456789012345678901234567890123456789"
|
||||
local s2 <const> = "01234567890123456789012345678901234567890123456789"
|
||||
local s3 = "01234567890123456789012345678901234567890123456789"
|
||||
local function foo() return s1 end
|
||||
local function foo1() return s3 end
|
||||
local function foo2()
|
||||
return "01234567890123456789012345678901234567890123456789"
|
||||
end
|
||||
local a1 = getadd(s1)
|
||||
assert(a1 == getadd(s2))
|
||||
assert(a1 == getadd(foo()))
|
||||
assert(a1 == getadd(foo1()))
|
||||
assert(a1 == getadd(foo2()))
|
||||
|
||||
local sd = "0123456789" .. "0123456789012345678901234567890123456789"
|
||||
assert(sd == s1 and getadd(sd) ~= a1)
|
||||
end
|
||||
|
||||
|
||||
-- testing line ends
|
||||
prog = [[
|
||||
a = 1 -- a comment
|
||||
b = 2
|
||||
local a = 1 -- a comment
|
||||
local b = 2
|
||||
|
||||
|
||||
x = [=[
|
||||
@@ -228,10 +254,11 @@ for _, n in pairs{"\n", "\r", "\n\r", "\r\n"} do
|
||||
assert(dostring(prog) == nn)
|
||||
assert(_G.x == "hi\n" and _G.y == "\nhello\r\n\n")
|
||||
end
|
||||
_G.x, _G.y = nil
|
||||
|
||||
|
||||
-- testing comments and strings with long brackets
|
||||
a = [==[]=]==]
|
||||
local a = [==[]=]==]
|
||||
assert(a == "]=")
|
||||
|
||||
a = [==[[===[[=[]]=][====[]]===]===]==]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
-- $Id: testes/locals.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
global <const> *
|
||||
|
||||
print('testing local variables and environments')
|
||||
|
||||
@@ -37,11 +39,13 @@ end
|
||||
f = nil
|
||||
|
||||
local f
|
||||
x = 1
|
||||
local x = 1
|
||||
|
||||
a = nil
|
||||
load('local a = {}')()
|
||||
assert(a == nil)
|
||||
do
|
||||
global a; a = nil
|
||||
load('local a = {}')()
|
||||
assert(a == nil)
|
||||
end
|
||||
|
||||
function f (a)
|
||||
local _1, _2, _3, _4, _5
|
||||
@@ -152,9 +156,9 @@ local dummy
|
||||
local _ENV = (function (...) return ... end)(_G, dummy) -- {
|
||||
|
||||
do local _ENV = {assert=assert}; assert(true) end
|
||||
mt = {_G = _G}
|
||||
local mt = {_G = _G}
|
||||
local foo,x
|
||||
A = false -- "declare" A
|
||||
global A; A = false -- "declare" A
|
||||
do local _ENV = mt
|
||||
function foo (x)
|
||||
A = x
|
||||
@@ -174,19 +178,30 @@ do local _ENV = {assert=assert, A=10};
|
||||
end
|
||||
assert(x==20)
|
||||
|
||||
A = nil
|
||||
|
||||
do -- constants
|
||||
|
||||
do print("testing local constants")
|
||||
global assert<const>, load, string, X
|
||||
X = 1 -- not a constant
|
||||
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("foo", "local<const> foo = 10; function foo() end")
|
||||
checkro("foo", "local<const> foo <const> = {}; function foo() end")
|
||||
checkro("foo", "global<const> foo <const>; function foo() end")
|
||||
checkro("XX", "global XX <const>; XX = 10")
|
||||
checkro("XX", "local _ENV; global XX <const>; XX = 10")
|
||||
|
||||
checkro("z", [[
|
||||
local a, z <const>, b = 10;
|
||||
@@ -197,11 +212,26 @@ do -- constants
|
||||
local a, var1 <const> = 10;
|
||||
function foo() a = 20; z = function () var1 = 12; end end
|
||||
]])
|
||||
|
||||
checkro("var1", [[
|
||||
global a, var1 <const>, z;
|
||||
local function foo() a = 20; z = function () var1 = 12; end end
|
||||
]])
|
||||
end
|
||||
|
||||
|
||||
|
||||
print"testing to-be-closed variables"
|
||||
|
||||
|
||||
do
|
||||
local st, msg = load("local <close> a, b")
|
||||
assert(not st and string.find(msg, "multiple"))
|
||||
|
||||
local st, msg = load("local a<close>, b<close>")
|
||||
assert(not st and string.find(msg, "multiple"))
|
||||
end
|
||||
|
||||
local function stack(n) n = ((n == 0) or stack(n - 1)) end
|
||||
|
||||
local function func2close (f, x, y)
|
||||
@@ -276,6 +306,32 @@ do
|
||||
end
|
||||
|
||||
|
||||
do -- testing presence of second argument
|
||||
local function foo (howtoclose, obj, n)
|
||||
local ca -- copy of 'a' visible inside its close metamethod
|
||||
do
|
||||
local a <close> = func2close(function (...)
|
||||
local t = table.pack(...)
|
||||
assert(select("#", ...) == n)
|
||||
assert(t.n == n and t[1] == ca and (t.n < 2 or t[2] == obj))
|
||||
ca = 15 -- final value to be returned if howtoclose=="scope"
|
||||
end)
|
||||
ca = a
|
||||
if howtoclose == "ret" then return obj -- 'a' closed by return
|
||||
elseif howtoclose == "err" then error(obj) -- 'a' closed by error
|
||||
end
|
||||
end -- 'a' closed by end of scope
|
||||
return ca -- ca now should be 15
|
||||
end
|
||||
-- with no errors, closing methods receive no extra argument
|
||||
assert(foo("scope", nil, 1) == 15) -- close by end of scope
|
||||
assert(foo("ret", 32, 1) == 32) -- close by return
|
||||
-- with errors, they do
|
||||
local st, msg = pcall(foo, "err", 23, 2) -- close by error
|
||||
assert(not st and msg == 23)
|
||||
end
|
||||
|
||||
|
||||
-- testing to-be-closed x compile-time constants
|
||||
-- (there were some bugs here in Lua 5.4-rc3, due to a confusion
|
||||
-- between compile levels and stack levels of variables)
|
||||
@@ -335,6 +391,49 @@ do
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
-- bug in 5.4.3: previous condition (calls cannot be tail in the
|
||||
-- scope of to-be-closed variables) must be valid for tbc variables
|
||||
-- created by 'for' loops.
|
||||
|
||||
local closed = false
|
||||
|
||||
local function foo ()
|
||||
return function () return true end, 0, 0,
|
||||
func2close(function () closed = true end)
|
||||
end
|
||||
|
||||
local function tail() return closed end
|
||||
|
||||
local function foo1 ()
|
||||
for k in foo() do return tail() end
|
||||
end
|
||||
|
||||
assert(foo1() == false)
|
||||
assert(closed == true)
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
-- bug in 5.4.4: 'break' may generate wrong 'close' instruction when
|
||||
-- leaving a loop block.
|
||||
|
||||
local closed = false
|
||||
|
||||
local o1 = setmetatable({}, {__close=function() closed = true end})
|
||||
|
||||
local function test()
|
||||
for k, v in next, {}, nil, o1 do
|
||||
local function f() return k end -- create an upvalue
|
||||
break
|
||||
end
|
||||
assert(closed)
|
||||
end
|
||||
|
||||
test()
|
||||
end
|
||||
|
||||
|
||||
do print("testing errors in __close")
|
||||
|
||||
-- original error is in __close
|
||||
@@ -567,6 +666,28 @@ end
|
||||
|
||||
if rawget(_G, "T") then
|
||||
|
||||
do
|
||||
-- bug in 5.4.3
|
||||
-- 'lua_settop' may use a pointer to stack invalidated by 'luaF_close'
|
||||
|
||||
-- reduce stack size
|
||||
collectgarbage(); collectgarbage(); collectgarbage()
|
||||
|
||||
-- force a stack reallocation
|
||||
local function loop (n)
|
||||
if n < 400 then loop(n + 1) end
|
||||
end
|
||||
|
||||
-- close metamethod will reallocate the stack
|
||||
local o = setmetatable({}, {__close = function () loop(0) end})
|
||||
|
||||
local script = [[toclose 2; settop 1; return 1]]
|
||||
|
||||
assert(T.testC(script, o) == script)
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- memory error inside closing function
|
||||
local function foo ()
|
||||
local y <close> = func2close(function () T.alloccount() end)
|
||||
@@ -644,7 +765,7 @@ if rawget(_G, "T") then
|
||||
|
||||
collectgarbage(); collectgarbage()
|
||||
|
||||
m = T.totalmem()
|
||||
local m = T.totalmem()
|
||||
collectgarbage("stop")
|
||||
|
||||
-- error in the first buffer allocation
|
||||
@@ -659,14 +780,8 @@ if rawget(_G, "T") then
|
||||
-- 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, buffer, buffer, final string
|
||||
T.totalmem(m + 4*lim + extra)
|
||||
-- userdata, buffer, final string
|
||||
T.totalmem(m + 2*lim + extra)
|
||||
assert(#table.concat(a) == 2*lim)
|
||||
|
||||
T.totalmem(0) -- remove memory limit
|
||||
@@ -790,6 +905,67 @@ do
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
-- yielding inside closing metamethods while returning
|
||||
-- (bug in 5.4.3)
|
||||
|
||||
local extrares -- result from extra yield (if any)
|
||||
|
||||
local function check (body, extra, ...)
|
||||
local t = table.pack(...) -- expected returns
|
||||
local co = coroutine.wrap(body)
|
||||
if extra then
|
||||
extrares = co() -- runs until first (extra) yield
|
||||
end
|
||||
local res = table.pack(co()) -- runs until "regular" yield
|
||||
-- regular yield will yield all values passed to the close function;
|
||||
-- without errors, that is only the object being closed.
|
||||
assert(res.n == 1 and type(res[1]) == "table")
|
||||
local res2 = table.pack(co()) -- runs until end of function
|
||||
assert(res2.n == t.n)
|
||||
for i = 1, #t do
|
||||
if t[i] == "x" then
|
||||
assert(res2[i] == res[1]) -- value that was closed
|
||||
else
|
||||
assert(res2[i] == t[i])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function foo ()
|
||||
local x <close> = func2close(coroutine.yield) -- "regular" yield
|
||||
local extra <close> = func2close(function (self)
|
||||
assert(self == extrares)
|
||||
coroutine.yield(100) -- first (extra) yield
|
||||
end)
|
||||
extrares = extra
|
||||
return table.unpack{10, x, 30}
|
||||
end
|
||||
check(foo, true, 10, "x", 30)
|
||||
assert(extrares == 100)
|
||||
|
||||
local function foo ()
|
||||
local x <close> = func2close(coroutine.yield) -- "regular" yield
|
||||
return
|
||||
end
|
||||
check(foo, false)
|
||||
|
||||
local function foo ()
|
||||
local x <close> = func2close(coroutine.yield) -- "regular" yield
|
||||
local y, z = 20, 30
|
||||
return x
|
||||
end
|
||||
check(foo, false, "x")
|
||||
|
||||
local function foo ()
|
||||
local x <close> = func2close(coroutine.yield) -- "regular" yield
|
||||
local extra <close> = func2close(coroutine.yield) -- extra yield
|
||||
return table.unpack({}, 1, 100) -- 100 nils
|
||||
end
|
||||
check(foo, true, table.unpack({}, 1, 100))
|
||||
|
||||
end
|
||||
|
||||
do
|
||||
-- yielding inside closing metamethods after an error
|
||||
|
||||
@@ -1012,7 +1188,7 @@ do
|
||||
local function open (x)
|
||||
numopen = numopen + 1
|
||||
return
|
||||
function () -- iteraction function
|
||||
function () -- iteration function
|
||||
x = x - 1
|
||||
if x > 0 then return x end
|
||||
end,
|
||||
|
||||
149
testes/main.lua
149
testes/main.lua
@@ -1,6 +1,6 @@
|
||||
# testing special comment on first line
|
||||
-- $Id: testes/main.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
-- most (all?) tests here assume a reasonable "Unix-like" shell
|
||||
if _port then return end
|
||||
@@ -27,17 +27,19 @@ do
|
||||
end
|
||||
print("progname: "..progname)
|
||||
|
||||
local prepfile = function (s, p)
|
||||
p = p or prog
|
||||
io.output(p)
|
||||
io.write(s)
|
||||
assert(io.close())
|
||||
|
||||
local prepfile = function (s, mod, p)
|
||||
mod = mod and "wb" or "w" -- mod true means binary files
|
||||
p = p or prog -- file to write the program
|
||||
local f = io.open(p, mod)
|
||||
f:write(s)
|
||||
assert(f:close())
|
||||
end
|
||||
|
||||
local function getoutput ()
|
||||
io.input(out)
|
||||
local t = io.read("a")
|
||||
io.input():close()
|
||||
local f = io.open(out)
|
||||
local t = f:read("a")
|
||||
f:close()
|
||||
assert(os.remove(out))
|
||||
return t
|
||||
end
|
||||
@@ -65,10 +67,11 @@ local function RUN (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'
|
||||
s = string.format("%s >%s 2>&1", s, out) -- send output and error to 'out'
|
||||
assert(not os.execute(s))
|
||||
assert(string.find(getoutput(), msg, 1, true)) -- check error message
|
||||
end
|
||||
@@ -94,6 +97,33 @@ RUN('echo "print(10)\nprint(2)\n" | lua > %s', out)
|
||||
checkout("10\n2\n")
|
||||
|
||||
|
||||
-- testing BOM
|
||||
prepfile("\xEF\xBB\xBF")
|
||||
RUN('lua %s > %s', prog, out)
|
||||
checkout("")
|
||||
|
||||
prepfile("\xEF\xBB\xBFprint(3)")
|
||||
RUN('lua %s > %s', prog, out)
|
||||
checkout("3\n")
|
||||
|
||||
prepfile("\xEF\xBB\xBF# comment!!\nprint(3)")
|
||||
RUN('lua %s > %s', prog, out)
|
||||
checkout("3\n")
|
||||
|
||||
-- bad BOMs
|
||||
prepfile("\xEF", true)
|
||||
NoRun("unexpected symbol", 'lua %s', prog)
|
||||
|
||||
prepfile("\xEF\xBB", true)
|
||||
NoRun("unexpected symbol", 'lua %s', prog)
|
||||
|
||||
prepfile("\xEFprint(3)", true)
|
||||
NoRun("unexpected symbol", 'lua %s', prog)
|
||||
|
||||
prepfile("\xEF\xBBprint(3)", true)
|
||||
NoRun("unexpected symbol", 'lua %s', prog)
|
||||
|
||||
|
||||
-- test option '-'
|
||||
RUN('echo "print(arg[1])" | lua - -h > %s', out)
|
||||
checkout("-h\n")
|
||||
@@ -107,7 +137,7 @@ 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)
|
||||
RUN('env LUA_INIT= LUA_PATH_5_5=y LUA_PATH=x lua %s > %s', prog, out)
|
||||
checkout("y\n")
|
||||
|
||||
-- test LUA_CPATH
|
||||
@@ -116,7 +146,7 @@ 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)
|
||||
RUN('env LUA_INIT= LUA_CPATH_5_5=yacc LUA_CPATH=x lua %s > %s', prog, out)
|
||||
checkout("yacc\n")
|
||||
|
||||
-- test LUA_INIT (and its access to 'arg' table)
|
||||
@@ -126,7 +156,7 @@ 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)
|
||||
RUN('env LUA_INIT_5_5="X=10" LUA_INIT="X=3" lua %s > %s', prog, out)
|
||||
checkout("10\n")
|
||||
|
||||
-- test LUA_INIT for files
|
||||
@@ -186,10 +216,22 @@ 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)
|
||||
prepfile(("print(a); print(_G['%s'].x)"):format(prog), false, 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 explicit global names in -l
|
||||
prepfile("print(str.upper'alo alo', m.max(10, 20))")
|
||||
RUN("lua -l 'str=string' '-lm=math' -e 'print(m.sin(0))' %s > %s", prog, out)
|
||||
checkout("0.0\nALO ALO\t20\n")
|
||||
|
||||
|
||||
-- test module names with version suffix ("libs/lib2-v2")
|
||||
RUN("env LUA_CPATH='./libs/?.so' lua -l lib2-v2 -e 'print(lib2.id())' > %s",
|
||||
out)
|
||||
checkout("true\n")
|
||||
|
||||
|
||||
-- test 'arg' table
|
||||
local a = [[
|
||||
assert(#arg == 3 and arg[1] == 'a' and
|
||||
@@ -205,7 +247,7 @@ 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)
|
||||
prepfile("assert(arg)", false, otherprog)
|
||||
RUN('env LUA_PATH="?;;" lua -l%s - < %s', prog, otherprog)
|
||||
|
||||
-- test messing up the 'arg' table
|
||||
@@ -221,6 +263,15 @@ assert(string.find(getoutput(), "error calling 'print'"))
|
||||
RUN('echo "io.stderr:write(1000)\ncont" | lua -e "require\'debug\'.debug()" 2> %s', out)
|
||||
checkout("lua_debug> 1000lua_debug> ")
|
||||
|
||||
do -- test warning for locals
|
||||
RUN('echo " local x" | lua -i > %s 2>&1', out)
|
||||
assert(string.find(getoutput(), "warning: "))
|
||||
|
||||
RUN('echo "local1 = 10\nlocal1 + 3" | lua -i > %s 2>&1', out)
|
||||
local t = getoutput()
|
||||
assert(not string.find(t, "warning"))
|
||||
assert(string.find(t, "13"))
|
||||
end
|
||||
|
||||
print("testing warnings")
|
||||
|
||||
@@ -256,6 +307,37 @@ u2 = setmetatable({}, {__gc = function () error("ZYX") end})
|
||||
RUN('lua -W %s 2> %s', prog, out)
|
||||
checkprogout("ZYX)\nXYZ)\n")
|
||||
|
||||
-- bug since 5.2: finalizer called when closing a state could
|
||||
-- subvert finalization order
|
||||
prepfile[[
|
||||
-- ensure tables will be collected only at the end of the program
|
||||
collectgarbage"stop"
|
||||
|
||||
print("creating 1")
|
||||
-- this finalizer should be called last
|
||||
setmetatable({}, {__gc = function () print(1) end})
|
||||
|
||||
print("creating 2")
|
||||
setmetatable({}, {__gc = function ()
|
||||
print("2")
|
||||
print("creating 3")
|
||||
-- this finalizer should not be called, as object will be
|
||||
-- created after 'lua_close' has been called
|
||||
setmetatable({}, {__gc = function () print(3) end})
|
||||
print(collectgarbage() or false) -- cannot call collector here
|
||||
os.exit(0, true)
|
||||
end})
|
||||
]]
|
||||
RUN('lua -W %s > %s', prog, out)
|
||||
checkout[[
|
||||
creating 1
|
||||
creating 2
|
||||
2
|
||||
creating 3
|
||||
false
|
||||
1
|
||||
]]
|
||||
|
||||
|
||||
-- test many arguments
|
||||
prepfile[[print(({...})[30])]]
|
||||
@@ -265,7 +347,7 @@ checkout("a\n")
|
||||
RUN([[lua "-eprint(1)" -ea=3 -e "print(a)" > %s]], out)
|
||||
checkout("1\n3\n")
|
||||
|
||||
-- test iteractive mode
|
||||
-- test interactive mode
|
||||
prepfile[[
|
||||
(6*2-6) -- ===
|
||||
a =
|
||||
@@ -275,11 +357,16 @@ 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")
|
||||
prepfile("a = [[b\nc\nd\ne]]\na")
|
||||
RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out)
|
||||
checkprogout("b\nc\nd\ne\n\n")
|
||||
|
||||
prompt = "alo"
|
||||
-- input interrupted in continuation line
|
||||
prepfile("a.\n")
|
||||
RUN([[lua -i < %s > /dev/null 2> %s]], prog, out)
|
||||
checkprogout("near <eof>\n")
|
||||
|
||||
local prompt = "alo"
|
||||
prepfile[[ --
|
||||
a = 2
|
||||
]]
|
||||
@@ -298,20 +385,18 @@ assert(string.find(t, prompt .. ".*" .. prompt .. ".*" .. prompt))
|
||||
|
||||
|
||||
-- non-string prompt
|
||||
prompt =
|
||||
"local C = 0;\z
|
||||
_PROMPT=setmetatable({},{__tostring = function () \z
|
||||
C = C + 1; return C end})"
|
||||
prompt = [[
|
||||
local C = 'X';
|
||||
_PROMPT=setmetatable({},{__tostring = function ()
|
||||
C = C .. 'X'; return C end})
|
||||
]]
|
||||
prepfile[[ --
|
||||
a = 2
|
||||
]]
|
||||
RUN([[lua -e "%s" -i < %s > %s]], prompt, prog, out)
|
||||
local t = getoutput()
|
||||
assert(string.find(t, [[
|
||||
1 --
|
||||
2a = 2
|
||||
3
|
||||
]], 1, true))
|
||||
-- skip version line and then check the presence of the three prompts
|
||||
assert(string.find(t, "^.-\nXX[^\nX]*\n?XXX[^\nX]*\n?XXXX\n?$"))
|
||||
|
||||
|
||||
-- test for error objects
|
||||
@@ -330,7 +415,7 @@ NoRun("error object is a table value", [[lua %s]], prog)
|
||||
|
||||
|
||||
-- chunk broken in many lines
|
||||
s = [=[ --
|
||||
local s = [=[ --
|
||||
function f ( x )
|
||||
local a = [[
|
||||
xuxu
|
||||
@@ -352,12 +437,10 @@ 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)
|
||||
]]
|
||||
-- first-line comment with binary file
|
||||
prepfile("#comment\n" .. string.dump(load("print(3)")), true)
|
||||
RUN('lua %s > %s', prog, out)
|
||||
checkprogout('3\n')
|
||||
checkout('3\n')
|
||||
|
||||
-- close Lua with an open file
|
||||
prepfile(string.format([[io.output(%q); io.write('alo')]], out))
|
||||
|
||||
134
testes/math.lua
134
testes/math.lua
@@ -1,10 +1,17 @@
|
||||
-- $Id: testes/math.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
print("testing numbers and math lib")
|
||||
|
||||
local minint <const> = math.mininteger
|
||||
local maxint <const> = math.maxinteger
|
||||
local math = require "math"
|
||||
local string = require "string"
|
||||
|
||||
global none
|
||||
|
||||
global<const> print, assert, pcall, type, pairs, load
|
||||
global<const> tonumber, tostring, select
|
||||
|
||||
local<const> minint, maxint = math.mininteger, math.maxinteger
|
||||
|
||||
local intbits <const> = math.floor(math.log(maxint, 2) + 0.5) + 1
|
||||
assert((1 << intbits) == 0)
|
||||
@@ -22,6 +29,18 @@ do
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- maximum exponent for a floating-point number
|
||||
local maxexp = 0
|
||||
do
|
||||
local p = 2.0
|
||||
while p < math.huge do
|
||||
maxexp = maxexp + 1
|
||||
p = p + p
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function isNaN (x)
|
||||
return (x ~= x)
|
||||
end
|
||||
@@ -34,8 +53,8 @@ do
|
||||
local x = 2.0^floatbits
|
||||
assert(x > x - 1.0 and x == x + 1.0)
|
||||
|
||||
print(string.format("%d-bit integers, %d-bit (mantissa) floats",
|
||||
intbits, floatbits))
|
||||
local msg = " %d-bit integers, %d-bit*2^%d floats"
|
||||
print(string.format(msg, intbits, floatbits, maxexp))
|
||||
end
|
||||
|
||||
assert(math.type(0) == "integer" and math.type(0.0) == "float"
|
||||
@@ -50,7 +69,7 @@ end
|
||||
local msgf2i = "number.* has no integer representation"
|
||||
|
||||
-- float equality
|
||||
function eq (a,b,limit)
|
||||
local function eq (a,b,limit)
|
||||
if not limit then
|
||||
if floatbits >= 50 then limit = 1E-11
|
||||
else limit = 1E-5
|
||||
@@ -62,7 +81,7 @@ end
|
||||
|
||||
|
||||
-- equality with types
|
||||
function eqT (a,b)
|
||||
local function eqT (a,b)
|
||||
return a == b and math.type(a) == math.type(b)
|
||||
end
|
||||
|
||||
@@ -83,7 +102,7 @@ end
|
||||
do
|
||||
local x = -1
|
||||
local mz = 0/x -- minus zero
|
||||
t = {[0] = 10, 20, 30, 40, 50}
|
||||
local t = {[0] = 10, 20, 30, 40, 50}
|
||||
assert(t[mz] == t[0] and t[-0] == t[0])
|
||||
end
|
||||
|
||||
@@ -172,7 +191,7 @@ do
|
||||
for i = -3, 3 do -- variables avoid constant folding
|
||||
for j = -3, 3 do
|
||||
-- domain errors (0^(-n)) are not portable
|
||||
if not _port or i ~= 0 or j > 0 then
|
||||
if not _ENV._port or i ~= 0 or j > 0 then
|
||||
assert(eq(i^j, 1 / i^(-j)))
|
||||
end
|
||||
end
|
||||
@@ -418,7 +437,7 @@ for i = 2,36 do
|
||||
assert(tonumber('\t10000000000\t', i) == i10)
|
||||
end
|
||||
|
||||
if not _soft then
|
||||
if not _ENV._soft then
|
||||
-- tests with very long numerals
|
||||
assert(tonumber("0x"..string.rep("f", 13)..".0") == 2.0^(4*13) - 1)
|
||||
assert(tonumber("0x"..string.rep("f", 150)..".0") == 2.0^(4*150) - 1)
|
||||
@@ -620,7 +639,7 @@ assert(maxint % -2 == -1)
|
||||
|
||||
-- non-portable tests because Windows C library cannot compute
|
||||
-- fmod(1, huge) correctly
|
||||
if not _port then
|
||||
if not _ENV._port then
|
||||
local function anan (x) assert(isNaN(x)) end -- assert Not a Number
|
||||
anan(0.0 % 0)
|
||||
anan(1.3 % 0)
|
||||
@@ -767,6 +786,7 @@ assert(a == '10' and b == '20')
|
||||
|
||||
do
|
||||
print("testing -0 and NaN")
|
||||
global rawset, undef
|
||||
local mz <const> = -0.0
|
||||
local z <const> = 0.0
|
||||
assert(mz == z)
|
||||
@@ -803,7 +823,11 @@ do
|
||||
end
|
||||
|
||||
|
||||
print("testing 'math.random'")
|
||||
--
|
||||
-- [[==================================================================
|
||||
print("testing 'math.random'")
|
||||
-- -===================================================================
|
||||
--
|
||||
|
||||
local random, max, min = math.random, math.max, math.min
|
||||
|
||||
@@ -849,6 +873,7 @@ do
|
||||
math.randomseed(x, y) -- again should repeat the state
|
||||
assert(math.random(0) == res)
|
||||
-- keep the random seed for following tests
|
||||
print(string.format("random seeds: %d, %d", x, y))
|
||||
end
|
||||
|
||||
do -- test random for floats
|
||||
@@ -1018,6 +1043,91 @@ assert(not pcall(random, minint + 1, minint))
|
||||
assert(not pcall(random, maxint, maxint - 1))
|
||||
assert(not pcall(random, maxint, minint))
|
||||
|
||||
-- ]]==================================================================
|
||||
|
||||
|
||||
--
|
||||
-- [[==================================================================
|
||||
print("testing precision of 'tostring'")
|
||||
-- -===================================================================
|
||||
--
|
||||
|
||||
-- number of decimal digits supported by float precision
|
||||
local decdig = math.floor(floatbits * math.log(2, 10))
|
||||
print(string.format(" %d-digit float numbers with full precision",
|
||||
decdig))
|
||||
-- number of decimal digits supported by integer precision
|
||||
local Idecdig = math.floor(math.log(maxint, 10))
|
||||
print(string.format(" %d-digit integer numbers with full precision",
|
||||
Idecdig))
|
||||
|
||||
do
|
||||
-- Any number should print so that reading it back gives itself:
|
||||
-- tonumber(tostring(x)) == x
|
||||
|
||||
-- Mersenne fractions
|
||||
local p = 1.0
|
||||
for i = 1, maxexp do
|
||||
p = p + p
|
||||
local x = 1 / (p - 1)
|
||||
assert(x == tonumber(tostring(x)))
|
||||
end
|
||||
|
||||
-- some random numbers in [0,1)
|
||||
for i = 1, 100 do
|
||||
local x = math.random()
|
||||
assert(x == tonumber(tostring(x)))
|
||||
end
|
||||
|
||||
-- different numbers should print differently.
|
||||
-- check pairs of floats with minimum detectable difference
|
||||
local p = floatbits - 1
|
||||
global ipairs
|
||||
for i = 1, maxexp - 1 do
|
||||
for _, i in ipairs{-i, i} do
|
||||
local x = 2^i
|
||||
local diff = 2^(i - p) -- least significant bit for 'x'
|
||||
local y = x + diff
|
||||
local fy = tostring(y)
|
||||
assert(x ~= y and tostring(x) ~= fy)
|
||||
assert(tonumber(fy) == y)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- "reasonable" numerals should be printed like themselves
|
||||
|
||||
-- create random float numerals with 5 digits, with a decimal point
|
||||
-- inserted in all places. (With more than 5, things like "0.00001"
|
||||
-- reformats like "1e-5".)
|
||||
for i = 1, 1000 do
|
||||
-- random numeral with 5 digits
|
||||
local x = string.format("%.5d", math.random(0, 99999))
|
||||
for i = 2, #x do
|
||||
-- insert decimal point at position 'i'
|
||||
local y = string.sub(x, 1, i - 1) .. "." .. string.sub(x, i, -1)
|
||||
y = string.gsub(y, "^0*(%d.-%d)0*$", "%1") -- trim extra zeros
|
||||
assert(y == tostring(tonumber(y)))
|
||||
end
|
||||
end
|
||||
|
||||
-- all-random floats
|
||||
local Fsz = string.packsize("n") -- size of floats in bytes
|
||||
|
||||
for i = 1, 400 do
|
||||
local s = string.pack("j", math.random(0)) -- a random string of bits
|
||||
while #s < Fsz do -- make 's' long enough
|
||||
s = s .. string.pack("j", math.random(0))
|
||||
end
|
||||
local n = string.unpack("n", s) -- read 's' as a float
|
||||
s = tostring(n)
|
||||
if string.find(s, "^%-?%d") then -- avoid NaN, inf, -inf
|
||||
assert(tonumber(s) == n)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
-- ]]==================================================================
|
||||
|
||||
|
||||
print('OK')
|
||||
|
||||
266
testes/memerr.lua
Normal file
266
testes/memerr.lua
Normal file
@@ -0,0 +1,266 @@
|
||||
-- $Id: testes/memerr.lua $
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
|
||||
local function checkerr (msg, f, ...)
|
||||
local stat, err = pcall(f, ...)
|
||||
assert(not stat and string.find(err, msg))
|
||||
end
|
||||
|
||||
if T==nil then
|
||||
(Message or print)
|
||||
('\n >>> testC not active: skipping memory error tests <<<\n')
|
||||
return
|
||||
end
|
||||
|
||||
print("testing memory-allocation errors")
|
||||
|
||||
local debug = require "debug"
|
||||
|
||||
local pack = table.pack
|
||||
|
||||
-- standard error message for memory errors
|
||||
local MEMERRMSG = "not enough memory"
|
||||
|
||||
|
||||
-- memory error in panic function
|
||||
T.totalmem(T.totalmem()+10000) -- set low memory limit (+10k)
|
||||
assert(T.checkpanic("newuserdata 20000") == MEMERRMSG)
|
||||
T.totalmem(0) -- restore high limit
|
||||
|
||||
|
||||
|
||||
-- {==================================================================
|
||||
-- Testing memory limits
|
||||
-- ===================================================================
|
||||
|
||||
checkerr("block too big", T.newuserdata, math.maxinteger)
|
||||
collectgarbage()
|
||||
local f = load"local a={}; for i=1,100000 do a[i]=i end"
|
||||
T.alloccount(10)
|
||||
checkerr(MEMERRMSG, f)
|
||||
T.alloccount() -- remove limit
|
||||
|
||||
|
||||
-- test memory errors; increase limit for maximum memory by steps,
|
||||
-- o that we get memory errors in all allocations of a given
|
||||
-- task, until there is enough memory to complete the task without
|
||||
-- errors.
|
||||
local function testbytes (s, f)
|
||||
collectgarbage()
|
||||
local M = T.totalmem()
|
||||
local oldM = M
|
||||
local a,b = nil
|
||||
while true do
|
||||
collectgarbage(); collectgarbage()
|
||||
T.totalmem(M)
|
||||
a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
|
||||
T.totalmem(0) -- remove limit
|
||||
if a and b == "OK" then break end -- stop when no more errors
|
||||
if b ~= "OK" and b ~= MEMERRMSG then -- not a memory error?
|
||||
error(a, 0) -- propagate it
|
||||
end
|
||||
M = M + 7 -- increase memory limit
|
||||
end
|
||||
print(string.format("minimum memory for %s: %d bytes", s, M - oldM))
|
||||
return a
|
||||
end
|
||||
|
||||
-- test memory errors; increase limit for number of allocations one
|
||||
-- by one, so that we get memory errors in all allocations of a given
|
||||
-- task, until there is enough allocations to complete the task without
|
||||
-- errors.
|
||||
|
||||
local function testalloc (s, f)
|
||||
collectgarbage()
|
||||
local M = 0
|
||||
local a,b = nil
|
||||
while true do
|
||||
collectgarbage(); collectgarbage()
|
||||
T.alloccount(M)
|
||||
a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
|
||||
T.alloccount() -- remove limit
|
||||
if a and b == "OK" then break end -- stop when no more errors
|
||||
if b ~= "OK" and b ~= MEMERRMSG then -- not a memory error?
|
||||
error(a, 0) -- propagate it
|
||||
end
|
||||
M = M + 1 -- increase allocation limit
|
||||
end
|
||||
print(string.format("minimum allocations for %s: %d allocations", s, M))
|
||||
return a
|
||||
end
|
||||
|
||||
|
||||
local function testamem (s, f)
|
||||
testalloc(s, f)
|
||||
return testbytes(s, f)
|
||||
end
|
||||
|
||||
|
||||
-- doing nothing
|
||||
b = testamem("doing nothing", function () return 10 end)
|
||||
assert(b == 10)
|
||||
|
||||
-- testing memory errors when creating a new state
|
||||
|
||||
testamem("state creation", function ()
|
||||
local st = T.newstate()
|
||||
if st then T.closestate(st) end -- close new state
|
||||
return st
|
||||
end)
|
||||
|
||||
testamem("empty-table creation", function ()
|
||||
return {}
|
||||
end)
|
||||
|
||||
testamem("string creation", function ()
|
||||
return "XXX" .. "YYY"
|
||||
end)
|
||||
|
||||
testamem("coroutine creation", function()
|
||||
return coroutine.create(print)
|
||||
end)
|
||||
|
||||
|
||||
-- testing to-be-closed variables
|
||||
testamem("to-be-closed variables", function()
|
||||
local flag
|
||||
do
|
||||
local x <close> =
|
||||
setmetatable({}, {__close = function () flag = true end})
|
||||
flag = false
|
||||
local x = {}
|
||||
end
|
||||
return flag
|
||||
end)
|
||||
|
||||
|
||||
-- testing threads
|
||||
|
||||
-- get main thread from registry
|
||||
local mt = T.testC("rawgeti R !M; return 1")
|
||||
assert(type(mt) == "thread" and coroutine.running() == mt)
|
||||
|
||||
|
||||
|
||||
local function expand (n,s)
|
||||
if n==0 then return "" end
|
||||
local e = string.rep("=", n)
|
||||
return string.format("T.doonnewstack([%s[ %s;\n collectgarbage(); %s]%s])\n",
|
||||
e, s, expand(n-1,s), e)
|
||||
end
|
||||
|
||||
G=0; collectgarbage(); a =collectgarbage("count")
|
||||
load(expand(20,"G=G+1"))()
|
||||
assert(G==20); collectgarbage(); -- assert(gcinfo() <= a+1)
|
||||
G = nil
|
||||
|
||||
testamem("running code on new thread", function ()
|
||||
return T.doonnewstack("local x=1") == 0 -- try to create thread
|
||||
end)
|
||||
|
||||
|
||||
-- testing memory x compiler
|
||||
|
||||
testamem("loadstring", function ()
|
||||
return load("x=1") -- try to do load a string
|
||||
end)
|
||||
|
||||
|
||||
local testprog = [[
|
||||
local function foo () return end
|
||||
local t = {"x"}
|
||||
AA = "aaa"
|
||||
for i = 1, #t do AA = AA .. t[i] end
|
||||
return true
|
||||
]]
|
||||
|
||||
-- testing memory x dofile
|
||||
_G.AA = nil
|
||||
local t =os.tmpname()
|
||||
local f = assert(io.open(t, "w"))
|
||||
f:write(testprog)
|
||||
f:close()
|
||||
testamem("dofile", function ()
|
||||
local a = loadfile(t)
|
||||
return a and a()
|
||||
end)
|
||||
assert(os.remove(t))
|
||||
assert(_G.AA == "aaax")
|
||||
|
||||
|
||||
-- other generic tests
|
||||
|
||||
testamem("gsub", function ()
|
||||
local a, b = string.gsub("alo alo", "(a)", function (x) return x..'b' end)
|
||||
return (a == 'ablo ablo')
|
||||
end)
|
||||
|
||||
testamem("dump/undump", function ()
|
||||
local a = load(testprog)
|
||||
local b = a and string.dump(a)
|
||||
a = b and load(b)
|
||||
return a and a()
|
||||
end)
|
||||
|
||||
_G.AA = nil
|
||||
|
||||
local t = os.tmpname()
|
||||
testamem("file creation", function ()
|
||||
local f = assert(io.open(t, 'w'))
|
||||
assert (not io.open"nomenaoexistente")
|
||||
io.close(f);
|
||||
return not loadfile'nomenaoexistente'
|
||||
end)
|
||||
assert(os.remove(t))
|
||||
|
||||
testamem("table creation", function ()
|
||||
local a, lim = {}, 10
|
||||
for i=1,lim do a[i] = i; a[i..'a'] = {} end
|
||||
return (type(a[lim..'a']) == 'table' and a[lim] == lim)
|
||||
end)
|
||||
|
||||
testamem("constructors", function ()
|
||||
local a = {10, 20, 30, 40, 50; a=1, b=2, c=3, d=4, e=5}
|
||||
return (type(a) == 'table' and a.e == 5)
|
||||
end)
|
||||
|
||||
local a = 1
|
||||
local close = nil
|
||||
testamem("closure creation", function ()
|
||||
function close (b)
|
||||
return function (x) return b + x end
|
||||
end
|
||||
return (close(2)(4) == 6)
|
||||
end)
|
||||
|
||||
testamem("using coroutines", function ()
|
||||
local a = coroutine.wrap(function ()
|
||||
coroutine.yield(string.rep("a", 10))
|
||||
return {}
|
||||
end)
|
||||
assert(string.len(a()) == 10)
|
||||
return a()
|
||||
end)
|
||||
|
||||
do -- auxiliary buffer
|
||||
local lim = 100
|
||||
local a = {}; for i = 1, lim do a[i] = "01234567890123456789" end
|
||||
testamem("auxiliary buffer", function ()
|
||||
return (#table.concat(a, ",") == 20*lim + lim - 1)
|
||||
end)
|
||||
end
|
||||
|
||||
testamem("growing stack", function ()
|
||||
local function foo (n)
|
||||
if n == 0 then return 1 else return 1 + foo(n - 1) end
|
||||
end
|
||||
return foo(100)
|
||||
end)
|
||||
|
||||
-- }==================================================================
|
||||
|
||||
|
||||
print "Ok"
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
-- $Id: testes/nextvar.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
global <const> *
|
||||
|
||||
print('testing tables, next, and for')
|
||||
|
||||
@@ -9,6 +11,38 @@ local function checkerror (msg, f, ...)
|
||||
end
|
||||
|
||||
|
||||
|
||||
----------------------------------------------------------------
|
||||
local function printTable (t)
|
||||
local a, h = T.querytab(t)
|
||||
print("array:")
|
||||
for i = 1, a do
|
||||
print("", T.querytab(t, i - 1))
|
||||
end
|
||||
print("hash:")
|
||||
for i = 1, h do
|
||||
print("", T.querytab(t, a + i - 1))
|
||||
end
|
||||
end
|
||||
----------------------------------------------------------------
|
||||
local function countentries (t)
|
||||
local e = 0
|
||||
for _ in pairs(t) do e = e + 1 end
|
||||
return e
|
||||
end
|
||||
----------------------------------------------------------------
|
||||
|
||||
|
||||
local function check (t, na, nh)
|
||||
if not T then return end
|
||||
local a, h = T.querytab(t)
|
||||
if a ~= na or h ~= nh then
|
||||
print(na, nh, a, h)
|
||||
assert(nil)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local a = {}
|
||||
|
||||
-- make sure table has lots of space in hash part
|
||||
@@ -20,6 +54,44 @@ for i=1,100 do
|
||||
assert(#a == i)
|
||||
end
|
||||
|
||||
|
||||
do -- rehash moving elements from array to hash
|
||||
local a = {}
|
||||
for i = 1, 100 do a[i] = i end
|
||||
check(a, 128, 0)
|
||||
|
||||
for i = 5, 95 do a[i] = nil end
|
||||
check(a, 128, 0)
|
||||
|
||||
a[129] = 1 -- force a re-hash
|
||||
check(a, 4, 8) -- keys larger than 4 go to the hash part
|
||||
|
||||
for i = 1, 4 do assert(a[i] == i) end
|
||||
for i = 5, 95 do assert(a[i] == nil) end
|
||||
for i = 96, 100 do assert(a[i] == i) end
|
||||
assert(a[129] == 1)
|
||||
end
|
||||
|
||||
|
||||
do -- growing hash part keeping array part
|
||||
local a = table.create(1000)
|
||||
check(a, 1000, 0)
|
||||
a.x = 10
|
||||
check(a, 1000, 1) -- array part keeps its elements
|
||||
end
|
||||
|
||||
|
||||
do -- "growing" length of a prebuilt table
|
||||
local N = 100
|
||||
local a = table.create(N)
|
||||
for i = 1, N do
|
||||
a[#a + 1] = true
|
||||
assert(#a == i)
|
||||
end
|
||||
check(a, N, 0)
|
||||
end
|
||||
|
||||
|
||||
-- testing ipairs
|
||||
local x = 0
|
||||
for k,v in ipairs{10,20,30;x=12} do
|
||||
@@ -43,6 +115,32 @@ assert(i == 4)
|
||||
assert(type(ipairs{}) == 'function' and ipairs{} == ipairs{})
|
||||
|
||||
|
||||
do -- overflow (must wrap-around)
|
||||
local f = ipairs{}
|
||||
local k, v = f({[math.mininteger] = 10}, math.maxinteger)
|
||||
assert(k == math.mininteger and v == 10)
|
||||
k, v = f({[math.mininteger] = 10}, k)
|
||||
assert(k == nil)
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
-- alternate insertions and deletions in an almost full hash.
|
||||
-- In versions pre-5.5, that causes constant rehashings and
|
||||
-- takes a long time to complete.
|
||||
local a = {}
|
||||
for i = 1, 2^11 - 1 do
|
||||
a[i .. ""] = true
|
||||
end
|
||||
|
||||
for i = 1, 1e5 do
|
||||
local key = i .. "."
|
||||
a[key] = true
|
||||
a[key] = nil
|
||||
end
|
||||
assert(countentries(a) == 2^11 - 1)
|
||||
end
|
||||
|
||||
if not T then
|
||||
(Message or print)
|
||||
('\n >>> testC not active: skipping tests for table sizes <<<\n')
|
||||
@@ -50,22 +148,14 @@ else --[
|
||||
-- testing table sizes
|
||||
|
||||
|
||||
local function mp2 (n) -- minimum power of 2 >= n
|
||||
-- minimum power of 2 (or zero) >= n
|
||||
local function mp2 (n)
|
||||
local mp = 2^math.ceil(math.log(n, 2))
|
||||
assert(n == 0 or (mp/2 < n and n <= mp))
|
||||
assert((mp == 0 or mp/2 < n) and n <= mp)
|
||||
return mp
|
||||
end
|
||||
|
||||
|
||||
local function check (t, na, nh)
|
||||
local a, h = T.querytab(t)
|
||||
if a ~= na or h ~= nh then
|
||||
print(na, nh, a, h)
|
||||
assert(nil)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- testing C library sizes
|
||||
do
|
||||
local s = 0
|
||||
@@ -76,7 +166,7 @@ end
|
||||
|
||||
-- testing constructor sizes
|
||||
local sizes = {0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17,
|
||||
30, 31, 32, 33, 34, 254, 255, 256, 500, 1000}
|
||||
30, 31, 32, 33, 34, 254, 255, 256, 257, 500, 1001}
|
||||
|
||||
for _, sa in ipairs(sizes) do -- 'sa' is size of the array part
|
||||
local arr = {"return {"}
|
||||
@@ -120,8 +210,9 @@ end
|
||||
|
||||
-- testing tables dynamically built
|
||||
local lim = 130
|
||||
local a = {}; a[2] = 1; check(a, 0, 1)
|
||||
a = {}; a[0] = 1; check(a, 0, 1); a[2] = 1; check(a, 0, 2)
|
||||
local a = {}; a[2] = 1; check(a, 2, 0)
|
||||
a = {}; a[0] = 1; check(a, 0, 1);
|
||||
a[2] = 1; check(a, 2, 1)
|
||||
a = {}; a[0] = 1; a[1] = 1; check(a, 1, 1)
|
||||
a = {}
|
||||
for i = 1,lim do
|
||||
@@ -137,31 +228,85 @@ for i = 1,lim do
|
||||
check(a, 0, mp2(i))
|
||||
end
|
||||
|
||||
a = {}
|
||||
for i=1,16 do a[i] = i end
|
||||
check(a, 16, 0)
|
||||
|
||||
-- insert and delete elements until a rehash occur. Caller must ensure
|
||||
-- that a rehash will change the shape of the table. Must repeat because
|
||||
-- the insertion may collide with the deleted element, and then there is
|
||||
-- no rehash.
|
||||
local function forcerehash (t)
|
||||
local na, nh = T.querytab(t)
|
||||
local i = 10000
|
||||
repeat
|
||||
i = i + 1
|
||||
t[i] = true
|
||||
t[i] = undef
|
||||
local nna, nnh = T.querytab(t)
|
||||
until nna ~= na or nnh ~= nh
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
local a = {}
|
||||
for i=1,16 do a[i] = i end
|
||||
check(a, 16, 0)
|
||||
for i=1,11 do a[i] = undef end
|
||||
for i=30,50 do a[i] = true; a[i] = undef end -- force a rehash (?)
|
||||
check(a, 0, 8) -- 5 elements in the table
|
||||
check(a, 16, 0)
|
||||
a[30] = true -- force a rehash
|
||||
a[30] = undef
|
||||
check(a, 0, 8) -- 5 elements in the hash part: [12]-[16]
|
||||
a[10] = 1
|
||||
for i=30,50 do a[i] = true; a[i] = undef end -- force a rehash (?)
|
||||
check(a, 0, 8) -- only 6 elements in the table
|
||||
forcerehash(a)
|
||||
check(a, 16, 1)
|
||||
for i=1,14 do a[i] = true; a[i] = undef end
|
||||
for i=18,50 do a[i] = true; a[i] = undef end -- force a rehash (?)
|
||||
check(a, 0, 4) -- only 2 elements ([15] and [16])
|
||||
check(a, 16, 1) -- no rehash...
|
||||
a[31] = true; a[32] = true -- force a rehash
|
||||
check(a, 0, 4) -- [15], [16], [31], [32]
|
||||
end
|
||||
|
||||
-- reverse filling
|
||||
for i=1,lim do
|
||||
do
|
||||
local N = 2^10
|
||||
local a = {}
|
||||
for i=i,1,-1 do a[i] = i end -- fill in reverse
|
||||
check(a, mp2(i), 0)
|
||||
for i = N, 1, -1 do a[i] = i end -- fill in reverse
|
||||
check(a, mp2(N), 0)
|
||||
end
|
||||
|
||||
|
||||
do -- "almost sparse" arrays
|
||||
-- create table with holes in 1/3 of its entries; all its
|
||||
-- elements are always in the array part
|
||||
local a = {}
|
||||
for i = 1, 257 do
|
||||
if i % 3 ~= 1 then
|
||||
a[i] = true
|
||||
check(a, mp2(i), 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
-- alternate insertions and deletions should give some extra
|
||||
-- space for the hash part. Otherwise, a mix of insertions/deletions
|
||||
-- could cause too many rehashes. (See the other test for "alternate
|
||||
-- insertions and deletions" in this file.)
|
||||
local a = {}
|
||||
for i = 1, 256 do
|
||||
a[i .. ""] = true
|
||||
end
|
||||
check(a, 0, 256) -- hash part is full
|
||||
a["256"] = nil -- delete a key
|
||||
forcerehash(a)
|
||||
-- table has only 255 elements, but it got some extra space;
|
||||
-- otherwise, almost each delete-insert would rehash the table again.
|
||||
assert(countentries(a) == 255)
|
||||
check(a, 0, 512)
|
||||
end
|
||||
|
||||
|
||||
-- size tests for vararg
|
||||
lim = 35
|
||||
function foo (n, ...)
|
||||
local function foo (n, ...)
|
||||
local arg = {...}
|
||||
check(arg, n, 0)
|
||||
assert(select('#', ...) == n)
|
||||
@@ -173,21 +318,6 @@ end
|
||||
local a = {}
|
||||
for i=1,lim do a[i] = true; foo(i, table.unpack(a)) end
|
||||
|
||||
|
||||
-- Table length with limit smaller than maximum value at array
|
||||
local a = {}
|
||||
for i = 1,64 do a[i] = true end -- make its array size 64
|
||||
for i = 1,64 do a[i] = nil end -- erase all elements
|
||||
assert(T.querytab(a) == 64) -- array part has 64 elements
|
||||
a[32] = true; a[48] = true; -- binary search will find these ones
|
||||
a[51] = true -- binary search will miss this one
|
||||
assert(#a == 48) -- this will set the limit
|
||||
assert(select(4, T.querytab(a)) == 48) -- this is the limit now
|
||||
a[50] = true -- this will set a new limit
|
||||
assert(select(4, T.querytab(a)) == 50) -- this is the limit now
|
||||
-- but the size is larger (and still inside the array part)
|
||||
assert(#a == 51)
|
||||
|
||||
end --]
|
||||
|
||||
|
||||
@@ -201,13 +331,24 @@ assert(#{1, 2, 3, nil, nil} == 3)
|
||||
print'+'
|
||||
|
||||
|
||||
do
|
||||
local s1, s2 = math.randomseed()
|
||||
print(string.format(
|
||||
"testing length for some random tables (seeds 0X%x:%x)", s1, s2))
|
||||
local N = 130
|
||||
for i = 1, 1e3 do -- create that many random tables
|
||||
local a = table.create(math.random(N)) -- initiate with random size
|
||||
for j = 1, math.random(N) do -- add random number of random entries
|
||||
a[math.random(N)] = true
|
||||
end
|
||||
assert(#a == 0 or a[#a] and not a[#a + 1])
|
||||
end
|
||||
end
|
||||
|
||||
local nofind = {}
|
||||
|
||||
a,b,c = 1,2,3
|
||||
a,b,c = nil
|
||||
|
||||
|
||||
-- next uses always the same iteraction function
|
||||
-- next uses always the same iteration function
|
||||
assert(next{} == next{})
|
||||
|
||||
local function find (name)
|
||||
@@ -254,7 +395,7 @@ for i=0,10000 do
|
||||
end
|
||||
end
|
||||
|
||||
n = {n=0}
|
||||
local n = {n=0}
|
||||
for i,v in pairs(a) do
|
||||
n.n = n.n+1
|
||||
assert(i and v and a[i] == v)
|
||||
@@ -499,6 +640,15 @@ do -- testing table library with metamethods
|
||||
end
|
||||
|
||||
|
||||
do -- testing overflow in table.insert (must wrap-around)
|
||||
|
||||
local t = setmetatable({},
|
||||
{__len = function () return math.maxinteger end})
|
||||
table.insert(t, 20)
|
||||
local k, v = next(t)
|
||||
assert(k == math.mininteger and v == 20)
|
||||
end
|
||||
|
||||
if not T then
|
||||
(Message or print)
|
||||
('\n >>> testC not active: skipping tests for table library on non-tables <<<\n')
|
||||
@@ -572,10 +722,12 @@ do
|
||||
a = 0; for i=1.0, 0.99999, -1 do a=a+1 end; assert(a==1)
|
||||
end
|
||||
|
||||
do -- changing the control variable
|
||||
local a
|
||||
a = 0; for i = 1, 10 do a = a + 1; i = "x" end; assert(a == 10)
|
||||
a = 0; for i = 10.0, 1, -1 do a = a + 1; i = "x" end; assert(a == 10)
|
||||
do -- attempt to change the control variable
|
||||
local st, msg = load "for i = 1, 10 do i = 10 end"
|
||||
assert(not st and string.find(msg, "assign to const variable 'i'"))
|
||||
|
||||
local st, msg = load "for v, k in pairs{} do v = 10 end"
|
||||
assert(not st and string.find(msg, "assign to const variable 'v'"))
|
||||
end
|
||||
|
||||
-- conversion
|
||||
@@ -764,4 +916,25 @@ for k,v in ipairs(a) do
|
||||
end
|
||||
assert(i == a.n)
|
||||
|
||||
|
||||
-- testing yield inside __pairs
|
||||
do
|
||||
local t = setmetatable({10, 20, 30}, {__pairs = function (t)
|
||||
local inc = coroutine.yield()
|
||||
return function (t, i)
|
||||
if i > 1 then return i - inc, t[i - inc] else return nil end
|
||||
end, t, #t + 1
|
||||
end})
|
||||
|
||||
local res = {}
|
||||
local co = coroutine.wrap(function ()
|
||||
for i,p in pairs(t) do res[#res + 1] = p end
|
||||
end)
|
||||
|
||||
co() -- start coroutine
|
||||
co(1) -- continue after yield
|
||||
assert(res[1] == 30 and res[2] == 20 and res[3] == 10 and #res == 3)
|
||||
|
||||
end
|
||||
|
||||
print"OK"
|
||||
|
||||
@@ -28,6 +28,7 @@ $NAME/literals.lua \
|
||||
$NAME/locals.lua \
|
||||
$NAME/main.lua \
|
||||
$NAME/math.lua \
|
||||
$NAME/memerr.lua \
|
||||
$NAME/nextvar.lua \
|
||||
$NAME/pm.lua \
|
||||
$NAME/sort.lua \
|
||||
|
||||
@@ -1,28 +1,33 @@
|
||||
-- $Id: testes/pm.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
-- UTF-8 file
|
||||
|
||||
|
||||
print('testing pattern matching')
|
||||
|
||||
global <const> *
|
||||
|
||||
local function checkerror (msg, f, ...)
|
||||
local s, err = pcall(f, ...)
|
||||
assert(not s and string.find(err, msg))
|
||||
end
|
||||
|
||||
|
||||
function f(s, p)
|
||||
local function f (s, p)
|
||||
local i,e = string.find(s, p)
|
||||
if i then return string.sub(s, i, e) end
|
||||
end
|
||||
|
||||
a,b = string.find('', '') -- empty patterns are tricky
|
||||
local a,b = string.find('', '') -- empty patterns are tricky
|
||||
assert(a == 1 and b == 0);
|
||||
a,b = string.find('alo', '')
|
||||
assert(a == 1 and b == 0)
|
||||
a,b = string.find('a\0o a\0o a\0o', 'a', 1) -- first position
|
||||
assert(a == 1 and b == 1)
|
||||
a,b = string.find('a\0o a\0o a\0o', 'a\0o', 2) -- starts in the midle
|
||||
a,b = string.find('a\0o a\0o a\0o', 'a\0o', 2) -- starts in the middle
|
||||
assert(a == 5 and b == 7)
|
||||
a,b = string.find('a\0o a\0o a\0o', 'a\0o', 9) -- starts in the midle
|
||||
a,b = string.find('a\0o a\0o a\0o', 'a\0o', 9) -- starts in the middle
|
||||
assert(a == 9 and b == 11)
|
||||
a,b = string.find('a\0a\0a\0a\0\0ab', '\0ab', 2); -- finds at the end
|
||||
assert(a == 9 and b == 11);
|
||||
@@ -50,6 +55,20 @@ assert(f('aLo_ALO', '%a*') == 'aLo')
|
||||
|
||||
assert(f(" \n\r*&\n\r xuxu \n\n", "%g%g%g+") == "xuxu")
|
||||
|
||||
|
||||
-- Adapt a pattern to UTF-8
|
||||
local function PU (p)
|
||||
-- distribute '?' into each individual byte of a character.
|
||||
-- (For instance, "á?" becomes "\195?\161?".)
|
||||
p = string.gsub(p, "(" .. utf8.charpattern .. ")%?", function (c)
|
||||
return string.gsub(c, ".", "%0?")
|
||||
end)
|
||||
-- change '.' to utf-8 character patterns
|
||||
p = string.gsub(p, "%.", utf8.charpattern)
|
||||
return p
|
||||
end
|
||||
|
||||
|
||||
assert(f('aaab', 'a*') == 'aaa');
|
||||
assert(f('aaa', '^.*$') == 'aaa');
|
||||
assert(f('aaa', 'b*') == '');
|
||||
@@ -73,22 +92,22 @@ assert(f('aaa', '^.-$') == 'aaa')
|
||||
assert(f('aabaaabaaabaaaba', 'b.*b') == 'baaabaaabaaab')
|
||||
assert(f('aabaaabaaabaaaba', 'b.-b') == 'baaab')
|
||||
assert(f('alo xo', '.o$') == 'xo')
|
||||
assert(f(' \n isto é assim', '%S%S*') == 'isto')
|
||||
assert(f(' \n isto é assim', '%S*$') == 'assim')
|
||||
assert(f(' \n isto é assim', '[a-z]*$') == 'assim')
|
||||
assert(f(' \n isto é assim', '%S%S*') == 'isto')
|
||||
assert(f(' \n isto é assim', '%S*$') == 'assim')
|
||||
assert(f(' \n isto é assim', '[a-z]*$') == 'assim')
|
||||
assert(f('um caracter ? extra', '[^%sa-z]') == '?')
|
||||
assert(f('', 'a?') == '')
|
||||
assert(f('á', 'á?') == 'á')
|
||||
assert(f('ábl', 'á?b?l?') == 'ábl')
|
||||
assert(f(' ábl', 'á?b?l?') == '')
|
||||
assert(f('á', PU'á?') == 'á')
|
||||
assert(f('ábl', PU'á?b?l?') == 'ábl')
|
||||
assert(f(' ábl', PU'á?b?l?') == '')
|
||||
assert(f('aa', '^aa?a?a') == 'aa')
|
||||
assert(f(']]]áb', '[^]]') == 'á')
|
||||
assert(f(']]]áb', '[^]]+') == 'áb')
|
||||
assert(f("0alo alo", "%x*") == "0a")
|
||||
assert(f("alo alo", "%C+") == "alo alo")
|
||||
print('+')
|
||||
|
||||
|
||||
function f1(s, p)
|
||||
local function f1 (s, p)
|
||||
p = string.gsub(p, "%%([0-9])", function (s)
|
||||
return "%" .. (tonumber(s)+1)
|
||||
end)
|
||||
@@ -113,7 +132,7 @@ local abc = string.char(range(0, 127)) .. string.char(range(128, 255));
|
||||
|
||||
assert(string.len(abc) == 256)
|
||||
|
||||
function strset (p)
|
||||
local function strset (p)
|
||||
local res = {s=''}
|
||||
string.gsub(abc, p, function (c) res.s = res.s .. c end)
|
||||
return res.s
|
||||
@@ -136,28 +155,28 @@ assert(string.match("alo xyzK", "(%w+)K") == "xyz")
|
||||
assert(string.match("254 K", "(%d*)K") == "")
|
||||
assert(string.match("alo ", "(%w*)$") == "")
|
||||
assert(not string.match("alo ", "(%w+)$"))
|
||||
assert(string.find("(álo)", "%(á") == 1)
|
||||
local a, b, c, d, e = string.match("âlo alo", "^(((.).).* (%w*))$")
|
||||
assert(a == 'âlo alo' and b == 'âl' and c == 'â' and d == 'alo' and e == nil)
|
||||
assert(string.find("(álo)", "%(á") == 1)
|
||||
local a, b, c, d, e = string.match("âlo alo", PU"^(((.).). (%w*))$")
|
||||
assert(a == 'âlo alo' and b == 'âl' and c == 'â' and d == 'alo' and e == nil)
|
||||
a, b, c, d = string.match('0123456789', '(.+(.?)())')
|
||||
assert(a == '0123456789' and b == '' and c == 11 and d == nil)
|
||||
print('+')
|
||||
|
||||
assert(string.gsub('ülo ülo', 'ü', 'x') == 'xlo xlo')
|
||||
assert(string.gsub('alo úlo ', ' +$', '') == 'alo úlo') -- trim
|
||||
assert(string.gsub('ülo ülo', 'ü', 'x') == 'xlo xlo')
|
||||
assert(string.gsub('alo úlo ', ' +$', '') == 'alo úlo') -- trim
|
||||
assert(string.gsub(' alo alo ', '^%s*(.-)%s*$', '%1') == 'alo alo') -- double trim
|
||||
assert(string.gsub('alo alo \n 123\n ', '%s+', ' ') == 'alo alo 123 ')
|
||||
t = "abç d"
|
||||
a, b = string.gsub(t, '(.)', '%1@')
|
||||
assert('@'..a == string.gsub(t, '', '@') and b == 5)
|
||||
a, b = string.gsub('abçd', '(.)', '%0@', 2)
|
||||
assert(a == 'a@b@çd' and b == 2)
|
||||
local t = "abç d"
|
||||
a, b = string.gsub(t, PU'(.)', '%1@')
|
||||
assert(a == "a@b@ç@ @d@" and b == 5)
|
||||
a, b = string.gsub('abçd', PU'(.)', '%0@', 2)
|
||||
assert(a == 'a@b@çd' and b == 2)
|
||||
assert(string.gsub('alo alo', '()[al]', '%1') == '12o 56o')
|
||||
assert(string.gsub("abc=xyz", "(%w*)(%p)(%w+)", "%3%2%1-%0") ==
|
||||
"xyz=abc-abc=xyz")
|
||||
assert(string.gsub("abc", "%w", "%1%0") == "aabbcc")
|
||||
assert(string.gsub("abc", "%w+", "%0%1") == "abcabc")
|
||||
assert(string.gsub('áéí', '$', '\0óú') == 'áéí\0óú')
|
||||
assert(string.gsub('áéí', '$', '\0óú') == 'áéí\0óú')
|
||||
assert(string.gsub('', '^', 'r') == 'r')
|
||||
assert(string.gsub('', '$', 'r') == 'r')
|
||||
print('+')
|
||||
@@ -184,31 +203,33 @@ do
|
||||
local function setglobal (n,v) rawset(_G, n, v) end
|
||||
string.gsub("a=roberto,roberto=a", "(%w+)=(%w%w*)", setglobal)
|
||||
assert(_G.a=="roberto" and _G.roberto=="a")
|
||||
_G.a = nil; _G.roberto = nil
|
||||
end
|
||||
|
||||
function f(a,b) return string.gsub(a,'.',b) end
|
||||
assert(string.gsub("trocar tudo em |teste|b| é |beleza|al|", "|([^|]*)|([^|]*)|", f) ==
|
||||
"trocar tudo em bbbbb é alalalalalal")
|
||||
assert(string.gsub("trocar tudo em |teste|b| é |beleza|al|", "|([^|]*)|([^|]*)|", f) ==
|
||||
"trocar tudo em bbbbb é alalalalalal")
|
||||
|
||||
local function dostring (s) return load(s, "")() or "" end
|
||||
assert(string.gsub("alo $a='x'$ novamente $return a$",
|
||||
"$([^$]*)%$",
|
||||
dostring) == "alo novamente x")
|
||||
|
||||
x = string.gsub("$x=string.gsub('alo', '.', string.upper)$ assim vai para $return x$",
|
||||
local x = string.gsub("$x=string.gsub('alo', '.', string.upper)$ assim vai para $return x$",
|
||||
"$([^$]*)%$", dostring)
|
||||
assert(x == ' assim vai para ALO')
|
||||
_G.a, _G.x = nil
|
||||
|
||||
t = {}
|
||||
s = 'a alo jose joao'
|
||||
r = string.gsub(s, '()(%w+)()', function (a,w,b)
|
||||
assert(string.len(w) == b-a);
|
||||
t[a] = b-a;
|
||||
end)
|
||||
local t = {}
|
||||
local s = 'a alo jose joao'
|
||||
local r = string.gsub(s, '()(%w+)()', function (a,w,b)
|
||||
assert(string.len(w) == b-a);
|
||||
t[a] = b-a;
|
||||
end)
|
||||
assert(s == r and t[1] == 1 and t[3] == 3 and t[7] == 4 and t[13] == 4)
|
||||
|
||||
|
||||
function isbalanced (s)
|
||||
local function isbalanced (s)
|
||||
return not string.find(string.gsub(s, "%b()", ""), "[()]")
|
||||
end
|
||||
|
||||
@@ -251,7 +272,7 @@ if not _soft then
|
||||
end
|
||||
|
||||
-- recursive nest of gsubs
|
||||
function rev (s)
|
||||
local function rev (s)
|
||||
return string.gsub(s, "(.)(.+)", function (c,s1) return rev(s1)..c end)
|
||||
end
|
||||
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
-- $Id: testes/sort.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
print "testing (parts of) table library"
|
||||
|
||||
print "testing unpack"
|
||||
|
||||
local unpack = table.unpack
|
||||
|
||||
local maxI = math.maxinteger
|
||||
local minI = math.mininteger
|
||||
|
||||
@@ -17,10 +13,44 @@ local function checkerror (msg, f, ...)
|
||||
end
|
||||
|
||||
|
||||
do print "testing 'table.create'"
|
||||
local N = 10000
|
||||
collectgarbage()
|
||||
local m = collectgarbage("count") * 1024
|
||||
local t = table.create(N)
|
||||
local memdiff = collectgarbage("count") * 1024 - m
|
||||
assert(memdiff > N * 4)
|
||||
for i = 1, 20 do
|
||||
assert(#t == i - 1)
|
||||
t[i] = 0
|
||||
end
|
||||
for i = 1, 20 do t[#t + 1] = i * 10 end
|
||||
assert(#t == 40 and t[39] == 190)
|
||||
assert(not T or T.querytab(t) == N)
|
||||
t = nil
|
||||
collectgarbage()
|
||||
m = collectgarbage("count") * 1024
|
||||
t = table.create(0, 1024)
|
||||
memdiff = collectgarbage("count") * 1024 - m
|
||||
assert(memdiff > 1024 * 12)
|
||||
assert(not T or select(2, T.querytab(t)) == 1024)
|
||||
|
||||
local maxint1 = 1 << (string.packsize("i") * 8 - 1)
|
||||
checkerror("out of range", table.create, maxint1)
|
||||
checkerror("out of range", table.create, 0, maxint1)
|
||||
checkerror("table overflow", table.create, 0, maxint1 - 1)
|
||||
end
|
||||
|
||||
|
||||
print "testing unpack"
|
||||
|
||||
local unpack = table.unpack
|
||||
|
||||
|
||||
checkerror("wrong number of arguments", table.insert, {}, 2, 3, 4)
|
||||
|
||||
local x,y,z,a,n
|
||||
a = {}; lim = _soft and 200 or 2000
|
||||
a = {}; local lim = _soft and 200 or 2000
|
||||
for i=1, lim do a[i]=i end
|
||||
assert(select(lim, unpack(a)) == lim and select('#', unpack(a)) == lim)
|
||||
x = unpack(a)
|
||||
@@ -169,7 +199,7 @@ do
|
||||
__index = function (_,k) pos1 = k end,
|
||||
__newindex = function (_,k) pos2 = k; error() end, })
|
||||
local st, msg = pcall(table.move, a, f, e, t)
|
||||
assert(not st and not msg and pos1 == x and pos2 == y)
|
||||
assert(not st and pos1 == x and pos2 == y)
|
||||
end
|
||||
checkmove(1, maxI, 0, 1, 0)
|
||||
checkmove(0, maxI - 1, 1, maxI - 1, maxI)
|
||||
@@ -222,7 +252,7 @@ a = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
|
||||
table.sort(a)
|
||||
check(a)
|
||||
|
||||
function perm (s, n)
|
||||
local function perm (s, n)
|
||||
n = n or #s
|
||||
if n == 1 then
|
||||
local t = {unpack(s)}
|
||||
@@ -248,7 +278,7 @@ perm{1,2,3,3,5}
|
||||
perm{1,2,3,4,5,6}
|
||||
perm{2,2,3,3,5,6}
|
||||
|
||||
function timesort (a, n, func, msg, pre)
|
||||
local function timesort (a, n, func, msg, pre)
|
||||
local x = os.clock()
|
||||
table.sort(a, func)
|
||||
x = (os.clock() - x) * 1000
|
||||
@@ -257,7 +287,7 @@ function timesort (a, n, func, msg, pre)
|
||||
check(a, func)
|
||||
end
|
||||
|
||||
limit = 50000
|
||||
local limit = 50000
|
||||
if _soft then limit = 5000 end
|
||||
|
||||
a = {}
|
||||
@@ -274,7 +304,7 @@ for i=1,limit do
|
||||
a[i] = math.random()
|
||||
end
|
||||
|
||||
x = os.clock(); i=0
|
||||
local x = os.clock(); local i = 0
|
||||
table.sort(a, function(x,y) i=i+1; return y<x end)
|
||||
x = (os.clock() - x) * 1000
|
||||
print(string.format("Invert-sorting other %d elements in %.2f msec., with %i comparisons",
|
||||
@@ -289,18 +319,19 @@ timesort(a, limit, function(x,y) return nil end, "equal")
|
||||
|
||||
for i,v in pairs(a) do assert(v == false) end
|
||||
|
||||
A = {"álo", "\0first :-)", "alo", "then this one", "45", "and a new"}
|
||||
table.sort(A)
|
||||
check(A)
|
||||
AA = {"\xE1lo", "\0first :-)", "alo", "then this one", "45", "and a new"}
|
||||
table.sort(AA)
|
||||
check(AA)
|
||||
|
||||
table.sort(A, function (x, y)
|
||||
load(string.format("A[%q] = ''", x), "")()
|
||||
table.sort(AA, function (x, y)
|
||||
load(string.format("AA[%q] = ''", x), "")()
|
||||
collectgarbage()
|
||||
return x<y
|
||||
end)
|
||||
|
||||
_G.AA = nil
|
||||
|
||||
tt = {__lt = function (a,b) return a.val < b.val end}
|
||||
local tt = {__lt = function (a,b) return a.val < b.val end}
|
||||
a = {}
|
||||
for i=1,10 do a[i] = {val=math.random(100)}; setmetatable(a[i], tt); end
|
||||
table.sort(a)
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
-- $Id: testes/strings.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
-- ISO Latin encoding
|
||||
|
||||
global <const> *
|
||||
|
||||
print('testing strings and string library')
|
||||
|
||||
@@ -52,7 +56,7 @@ assert(("\000123456789"):sub(8) == "789")
|
||||
|
||||
-- testing string.find
|
||||
assert(string.find("123456789", "345") == 3)
|
||||
a,b = string.find("123456789", "345")
|
||||
local a,b = string.find("123456789", "345")
|
||||
assert(string.sub("123456789", a, b) == "345")
|
||||
assert(string.find("1234567890123456789", "345", 3) == 3)
|
||||
assert(string.find("1234567890123456789", "345", 4) == 13)
|
||||
@@ -106,10 +110,9 @@ assert(string.rep('teste', 0) == '')
|
||||
assert(string.rep('tés\00tê', 2) == 'tés\0têtés\000tê')
|
||||
assert(string.rep('', 10) == '')
|
||||
|
||||
if string.packsize("i") == 4 then
|
||||
-- result length would be 2^31 (int overflow)
|
||||
checkerror("too large", string.rep, 'aa', (1 << 30))
|
||||
checkerror("too large", string.rep, 'a', (1 << 30), ',')
|
||||
do
|
||||
checkerror("too large", string.rep, 'aa', math.maxinteger);
|
||||
checkerror("too large", string.rep, 'a', math.maxinteger, ',')
|
||||
end
|
||||
|
||||
-- repetitions with separator
|
||||
@@ -154,6 +157,12 @@ else -- compatible coercion
|
||||
assert(tostring(-1203 + 0.0) == "-1203")
|
||||
end
|
||||
|
||||
|
||||
local function topointer (s)
|
||||
return string.format("%p", s)
|
||||
end
|
||||
|
||||
|
||||
do -- tests for '%p' format
|
||||
-- not much to test, as C does not specify what '%p' does.
|
||||
-- ("The value of the pointer is converted to a sequence of printing
|
||||
@@ -177,22 +186,22 @@ do -- tests for '%p' format
|
||||
|
||||
do
|
||||
local t1 = {}; local t2 = {}
|
||||
assert(string.format("%p", t1) ~= string.format("%p", t2))
|
||||
assert(topointer(t1) ~= topointer(t2))
|
||||
end
|
||||
|
||||
do -- short strings are internalized
|
||||
local s1 = string.rep("a", 10)
|
||||
local s2 = string.rep("aa", 5)
|
||||
assert(string.format("%p", s1) == string.format("%p", s2))
|
||||
assert(topointer(s1) == topointer(s2))
|
||||
end
|
||||
|
||||
do -- long strings aren't internalized
|
||||
local s1 = string.rep("a", 300); local s2 = string.rep("a", 300)
|
||||
assert(string.format("%p", s1) ~= string.format("%p", s2))
|
||||
assert(topointer(s1) ~= topointer(s2))
|
||||
end
|
||||
end
|
||||
|
||||
x = '"ílo"\n\\'
|
||||
local x = '"ílo"\n\\'
|
||||
assert(string.format('%q%s', x, x) == '"\\"ílo\\"\\\n\\\\""ílo"\n\\')
|
||||
assert(string.format('%q', "\0") == [["\0"]])
|
||||
assert(load(string.format('return %q', x))() == x)
|
||||
@@ -202,13 +211,11 @@ assert(string.format("\0%c\0%c%x\0", string.byte("\xe4"), string.byte("b"), 140)
|
||||
"\0\xe4\0b8c\0")
|
||||
assert(string.format('') == "")
|
||||
assert(string.format("%c",34)..string.format("%c",48)..string.format("%c",90)..string.format("%c",100) ==
|
||||
string.format("%c%c%c%c", 34, 48, 90, 100))
|
||||
string.format("%1c%-c%-1c%c", 34, 48, 90, 100))
|
||||
assert(string.format("%s\0 is not \0%s", 'not be', 'be') == 'not be\0 is not \0be')
|
||||
assert(string.format("%%%d %010d", 10, 23) == "%10 0000000023")
|
||||
assert(tonumber(string.format("%f", 10.3)) == 10.3)
|
||||
x = string.format('"%-50s"', 'a')
|
||||
assert(#x == 52)
|
||||
assert(string.sub(x, 1, 4) == '"a ')
|
||||
assert(string.format('"%-50s"', 'a') == '"a' .. string.rep(' ', 49) .. '"')
|
||||
|
||||
assert(string.format("-%.20s.20s", string.rep("%", 2000)) ==
|
||||
"-"..string.rep("%", 20)..".20s")
|
||||
@@ -237,7 +244,6 @@ end
|
||||
|
||||
assert(string.format("\0%s\0", "\0\0\1") == "\0\0\0\1\0")
|
||||
checkerror("contains zeros", string.format, "%10s", "\0")
|
||||
checkerror("cannot have modifiers", string.format, "%10q", "1")
|
||||
|
||||
-- format x tostring
|
||||
assert(string.format("%s %s", nil, true) == "nil true")
|
||||
@@ -341,6 +347,26 @@ do print("testing 'format %a %A'")
|
||||
end
|
||||
|
||||
|
||||
-- testing some flags (all these results are required by ISO C)
|
||||
assert(string.format("%#12o", 10) == " 012")
|
||||
assert(string.format("%#10x", 100) == " 0x64")
|
||||
assert(string.format("%#-17X", 100) == "0X64 ")
|
||||
assert(string.format("%013i", -100) == "-000000000100")
|
||||
assert(string.format("%2.5d", -100) == "-00100")
|
||||
assert(string.format("%.u", 0) == "")
|
||||
assert(string.format("%+#014.0f", 100) == "+000000000100.")
|
||||
assert(string.format("%-16c", 97) == "a ")
|
||||
assert(string.format("%+.3G", 1.5) == "+1.5")
|
||||
assert(string.format("%.0s", "alo") == "")
|
||||
assert(string.format("%.s", "alo") == "")
|
||||
|
||||
-- ISO C89 says that "The exponent always contains at least two digits",
|
||||
-- but unlike ISO C99 it does not ensure that it contains "only as many
|
||||
-- more digits as necessary".
|
||||
assert(string.match(string.format("% 1.0E", 100), "^ 1E%+0+2$"))
|
||||
assert(string.match(string.format("% .1g", 2^10), "^ 1e%+0+3$"))
|
||||
|
||||
|
||||
-- errors in format
|
||||
|
||||
local function check (fmt, msg)
|
||||
@@ -348,13 +374,21 @@ local function check (fmt, msg)
|
||||
end
|
||||
|
||||
local aux = string.rep('0', 600)
|
||||
check("%100.3d", "too long")
|
||||
check("%100.3d", "invalid conversion")
|
||||
check("%1"..aux..".3d", "too long")
|
||||
check("%1.100d", "too long")
|
||||
check("%1.100d", "invalid conversion")
|
||||
check("%10.1"..aux.."004d", "too long")
|
||||
check("%t", "invalid conversion")
|
||||
check("%"..aux.."d", "repeated flags")
|
||||
check("%"..aux.."d", "too long")
|
||||
check("%d %d", "no value")
|
||||
check("%010c", "invalid conversion")
|
||||
check("%.10c", "invalid conversion")
|
||||
check("%0.34s", "invalid conversion")
|
||||
check("%#i", "invalid conversion")
|
||||
check("%3.1p", "invalid conversion")
|
||||
check("%0.s", "invalid conversion")
|
||||
check("%10q", "cannot have modifiers")
|
||||
check("%F", "invalid conversion") -- useless and not in C89
|
||||
|
||||
|
||||
assert(load("return 1\n--comment without ending EOL")() == 1)
|
||||
@@ -427,7 +461,7 @@ end
|
||||
do
|
||||
local f = string.gmatch("1 2 3 4 5", "%d+")
|
||||
assert(f() == "1")
|
||||
co = coroutine.wrap(f)
|
||||
local co = coroutine.wrap(f)
|
||||
assert(co() == "2")
|
||||
end
|
||||
|
||||
@@ -493,6 +527,20 @@ else
|
||||
testpfs("P", str, {})
|
||||
end
|
||||
|
||||
if T == nil then
|
||||
(Message or print)('\n >>> testC not active: skipping external strings tests <<<\n')
|
||||
else
|
||||
print("testing external strings")
|
||||
local x = T.externKstr("hello") -- external fixed short string
|
||||
assert(x == "hello")
|
||||
local x = T.externstr("hello") -- external allocated short string
|
||||
assert(x == "hello")
|
||||
x = string.rep("a", 100) -- long string
|
||||
local y = T.externKstr(x) -- external fixed long string
|
||||
assert(y == x)
|
||||
local z = T.externstr(x) -- external allocated long string
|
||||
assert(z == y)
|
||||
end
|
||||
|
||||
print('OK')
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- $Id: testes/tpack.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
local pack = string.pack
|
||||
local packsize = string.packsize
|
||||
@@ -35,7 +35,7 @@ print("\talignment: " .. align)
|
||||
|
||||
|
||||
-- check errors in arguments
|
||||
function checkerror (msg, f, ...)
|
||||
local function checkerror (msg, f, ...)
|
||||
local status, err = pcall(f, ...)
|
||||
-- print(status, err, msg)
|
||||
assert(not status and string.find(err, msg))
|
||||
@@ -135,15 +135,15 @@ checkerror("variable%-length format", packsize, "z")
|
||||
-- overflow in option size (error will be in digit after limit)
|
||||
checkerror("invalid format", packsize, "c1" .. string.rep("0", 40))
|
||||
|
||||
if packsize("i") == 4 then
|
||||
-- result would be 2^31 (2^3 repetitions of 2^28 strings)
|
||||
local s = string.rep("c268435456", 2^3)
|
||||
checkerror("too large", packsize, s)
|
||||
-- one less is OK
|
||||
s = string.rep("c268435456", 2^3 - 1) .. "c268435455"
|
||||
assert(packsize(s) == 0x7fffffff)
|
||||
do
|
||||
local maxsize = (packsize("j") <= packsize("T")) and
|
||||
math.maxinteger or (1 << (packsize("T") * 8))
|
||||
assert (packsize(string.format("c%d", maxsize - 9)) == maxsize - 9)
|
||||
checkerror("too large", packsize, string.format("c%dc10", maxsize - 9))
|
||||
checkerror("too long", pack, string.format("xxxxxxxxxx c%d", maxsize - 9))
|
||||
end
|
||||
|
||||
|
||||
-- overflow in packing
|
||||
for i = 1, sizeLI - 1 do
|
||||
local umax = (1 << (i * 8)) - 1
|
||||
@@ -229,8 +229,9 @@ do
|
||||
assert(pack("c3", "123") == "123")
|
||||
assert(pack("c0", "") == "")
|
||||
assert(pack("c8", "123456") == "123456\0\0")
|
||||
assert(pack("c88", "") == string.rep("\0", 88))
|
||||
assert(pack("c188", "ab") == "ab" .. string.rep("\0", 188 - 2))
|
||||
assert(pack("c88 c1", "", "X") == string.rep("\0", 88) .. "X")
|
||||
assert(pack("c188 c2", "ab", "X\1") ==
|
||||
"ab" .. string.rep("\0", 188 - 2) .. "X\1")
|
||||
local a, b, c = unpack("!4 z c3", "abcdefghi\0xyz")
|
||||
assert(a == "abcdefghi" and b == "xyz" and c == 14)
|
||||
checkerror("longer than", pack, "c3", "1234")
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
-- $Id: testes/utf8.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
-- UTF-8 file
|
||||
|
||||
global <const> *
|
||||
|
||||
print "testing UTF-8 library"
|
||||
|
||||
@@ -50,25 +54,35 @@ local function check (s, t, nonstrict)
|
||||
for i = 1, #t do assert(t[i] == t1[i]) end -- 't' is equal to 't1'
|
||||
|
||||
for i = 1, l do -- for all codepoints
|
||||
local pi = utf8.offset(s, i) -- position of i-th char
|
||||
local pi, pie = utf8.offset(s, i) -- position of i-th char
|
||||
local pi1 = utf8.offset(s, 2, pi) -- position of next char
|
||||
assert(pi1 == pie + 1)
|
||||
assert(string.find(string.sub(s, pi, pi1 - 1), justone))
|
||||
assert(utf8.offset(s, -1, pi1) == pi)
|
||||
assert(utf8.offset(s, i - l - 1) == pi)
|
||||
assert(pi1 - pi == #utf8.char(utf8.codepoint(s, pi, pi, nonstrict)))
|
||||
for j = pi, pi1 - 1 do
|
||||
assert(utf8.offset(s, 0, j) == pi)
|
||||
local off1, off2 = utf8.offset(s, 0, j)
|
||||
assert(off1 == pi and off2 == pi1 - 1)
|
||||
end
|
||||
for j = pi + 1, pi1 - 1 do
|
||||
assert(not utf8.len(s, j))
|
||||
end
|
||||
assert(utf8.len(s, pi, pi, nonstrict) == 1)
|
||||
assert(utf8.len(s, pi, pi1 - 1, nonstrict) == 1)
|
||||
assert(utf8.len(s, pi, -1, nonstrict) == l - i + 1)
|
||||
assert(utf8.len(s, pi1, -1, nonstrict) == l - i)
|
||||
assert(utf8.len(s, 1, pi, nonstrict) == i)
|
||||
assert(utf8.len(s, pi, pi, nonstrict) == 1)
|
||||
assert(utf8.len(s, pi, pi1 - 1, nonstrict) == 1)
|
||||
assert(utf8.len(s, pi, -1, nonstrict) == l - i + 1)
|
||||
assert(utf8.len(s, pi1, -1, nonstrict) == l - i)
|
||||
assert(utf8.len(s, 1, pi, nonstrict) == i)
|
||||
end
|
||||
|
||||
local expected = 1 -- expected position of "current" character
|
||||
for i = 1, l + 1 do
|
||||
local p, e = utf8.offset(s, i)
|
||||
assert(p == expected)
|
||||
expected = e + 1
|
||||
end
|
||||
assert(expected - 1 == #s + 1)
|
||||
|
||||
local i = 0
|
||||
for p, c in utf8.codes(s, nonstrict) do
|
||||
i = i + 1
|
||||
@@ -92,14 +106,20 @@ end
|
||||
|
||||
|
||||
do -- error indication in utf8.len
|
||||
local function check (s, p)
|
||||
local function checklen (s, p)
|
||||
local a, b = utf8.len(s)
|
||||
assert(not a and b == p)
|
||||
end
|
||||
check("abc\xE3def", 4)
|
||||
check("汉字\x80", #("汉字") + 1)
|
||||
check("\xF4\x9F\xBF", 1)
|
||||
check("\xF4\x9F\xBF\xBF", 1)
|
||||
checklen("abc\xE3def", 4)
|
||||
checklen("\xF4\x9F\xBF", 1)
|
||||
checklen("\xF4\x9F\xBF\xBF", 1)
|
||||
-- spurious continuation bytes
|
||||
checklen("汉字\x80", #("汉字") + 1)
|
||||
checklen("\x80hello", 1)
|
||||
checklen("hel\x80lo", 4)
|
||||
checklen("汉字\xBF", #("汉字") + 1)
|
||||
checklen("\xBFhello", 1)
|
||||
checklen("hel\xBFlo", 4)
|
||||
end
|
||||
|
||||
-- errors in utf8.codes
|
||||
@@ -112,6 +132,16 @@ do
|
||||
end
|
||||
errorcodes("ab\xff")
|
||||
errorcodes("\u{110000}")
|
||||
errorcodes("in\x80valid")
|
||||
errorcodes("\xbfinvalid")
|
||||
errorcodes("αλφ\xBFα")
|
||||
|
||||
-- calling iteration function with invalid arguments
|
||||
local f = utf8.codes("")
|
||||
assert(f("", 2) == nil)
|
||||
assert(f("", -1) == nil)
|
||||
assert(f("", math.mininteger) == nil)
|
||||
|
||||
end
|
||||
|
||||
-- error in initial position for offset
|
||||
@@ -214,7 +244,7 @@ do
|
||||
check(s, {0x10000, 0x1FFFFF}, true)
|
||||
end
|
||||
|
||||
x = "日本語a-4\0éó"
|
||||
local x = "日本語a-4\0éó"
|
||||
check(x, {26085, 26412, 35486, 97, 45, 52, 0, 233, 243})
|
||||
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
-- $Id: testes/vararg.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
print('testing vararg')
|
||||
|
||||
function f(a, ...)
|
||||
local function f (a, ...)
|
||||
local x = {n = select('#', ...), ...}
|
||||
for i = 1, x.n do assert(a[i] == x[i]) end
|
||||
return x.n
|
||||
end
|
||||
|
||||
function c12 (...)
|
||||
local function c12 (...)
|
||||
assert(arg == _G.arg) -- no local 'arg'
|
||||
local x = {...}; x.n = #x
|
||||
local res = (x.n==2 and x[1] == 1 and x[2] == 2)
|
||||
@@ -17,7 +17,7 @@ function c12 (...)
|
||||
return res, 2
|
||||
end
|
||||
|
||||
function vararg (...) return {n = select('#', ...), ...} end
|
||||
local function vararg (...) return {n = select('#', ...), ...} end
|
||||
|
||||
local call = function (f, args) return f(table.unpack(args, 1, args.n)) end
|
||||
|
||||
@@ -29,7 +29,7 @@ assert(vararg().n == 0)
|
||||
assert(vararg(nil, nil).n == 2)
|
||||
|
||||
assert(c12(1,2)==55)
|
||||
a,b = assert(call(c12, {1,2}))
|
||||
local a,b = assert(call(c12, {1,2}))
|
||||
assert(a == 55 and b == 2)
|
||||
a = call(c12, {1,2;n=2})
|
||||
assert(a == 55 and b == 2)
|
||||
@@ -49,7 +49,7 @@ function t:f (...) local arg = {...}; return self[...]+#arg end
|
||||
assert(t:f(1,4) == 3 and t:f(2) == 11)
|
||||
print('+')
|
||||
|
||||
lim = 20
|
||||
local lim = 20
|
||||
local i, a = 1, {}
|
||||
while i <= lim do a[i] = i+0.3; i=i+1 end
|
||||
|
||||
@@ -59,7 +59,7 @@ function f(a, b, c, d, ...)
|
||||
more[lim-4] == lim+0.3 and not more[lim-3])
|
||||
end
|
||||
|
||||
function g(a,b,c)
|
||||
local function g (a,b,c)
|
||||
assert(a == 1.3 and b == 2.3 and c == 3.3)
|
||||
end
|
||||
|
||||
@@ -76,7 +76,7 @@ print("+")
|
||||
|
||||
-- new-style varargs
|
||||
|
||||
function oneless (a, ...) return ... end
|
||||
local function oneless (a, ...) return ... end
|
||||
|
||||
function f (n, a, ...)
|
||||
local b
|
||||
@@ -99,8 +99,8 @@ assert(a==nil and b==nil and c==nil and d==nil and e==nil)
|
||||
|
||||
|
||||
-- varargs for main chunks
|
||||
f = load[[ return {...} ]]
|
||||
x = f(2,3)
|
||||
local f = load[[ return {...} ]]
|
||||
local x = f(2,3)
|
||||
assert(x[1] == 2 and x[2] == 3 and x[3] == undef)
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- $Id: testes/verybig.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
print "testing RK"
|
||||
|
||||
@@ -52,7 +52,7 @@ if _soft then return 10 end
|
||||
print "testing large programs (>64k)"
|
||||
|
||||
-- template to create a very big test file
|
||||
prog = [[$
|
||||
local prog = [[$
|
||||
|
||||
local a,b
|
||||
|
||||
@@ -85,7 +85,7 @@ function b:xxx (a,b) return a+b end
|
||||
assert(b:xxx(10, 12) == 22) -- pushself with non-constant index
|
||||
b["xxx"] = undef
|
||||
|
||||
s = 0; n=0
|
||||
local s = 0; local n=0
|
||||
for a,b in pairs(b) do s=s+b; n=n+1 end
|
||||
-- with 32-bit floats, exact value of 's' depends on summation order
|
||||
assert(81800000.0 < s and s < 81860000 and n == 70001)
|
||||
@@ -93,7 +93,7 @@ assert(81800000.0 < s and s < 81860000 and n == 70001)
|
||||
a = nil; b = nil
|
||||
print'+'
|
||||
|
||||
function f(x) b=x end
|
||||
local function f(x) b=x end
|
||||
|
||||
a = f{$3$} or 10
|
||||
|
||||
@@ -118,7 +118,7 @@ local function sig (x)
|
||||
return (x % 2 == 0) and '' or '-'
|
||||
end
|
||||
|
||||
F = {
|
||||
local F = {
|
||||
function () -- $1$
|
||||
for i=10,50009 do
|
||||
io.write('a', i, ' = ', sig(i), 5+((i-10)/2), ',\n')
|
||||
@@ -138,14 +138,14 @@ function () -- $3$
|
||||
end,
|
||||
}
|
||||
|
||||
file = os.tmpname()
|
||||
local file = os.tmpname()
|
||||
io.output(file)
|
||||
for s in string.gmatch(prog, "$([^$]+)") do
|
||||
local n = tonumber(s)
|
||||
if not n then io.write(s) else F[n]() end
|
||||
end
|
||||
io.close()
|
||||
result = dofile(file)
|
||||
local result = dofile(file)
|
||||
assert(os.remove(file))
|
||||
print'OK'
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user