diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2023-09-05 15:30:45 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2023-09-05 15:30:45 -0300 |
| commit | 14e416355f83cf0a1b871eedec2c92b86dbe76d6 (patch) | |
| tree | 620c7fa0b811d5f91d3d2f9b4879b289df6d137c | |
| parent | f33cda8d6eb1cac5b9042429e85f1096175c7ca5 (diff) | |
| download | lua-14e416355f83cf0a1b871eedec2c92b86dbe76d6.tar.gz lua-14e416355f83cf0a1b871eedec2c92b86dbe76d6.tar.bz2 lua-14e416355f83cf0a1b871eedec2c92b86dbe76d6.zip | |
Added suport for Fixed Buffers
A fixed buffer keeps a binary chunk "forever", so that the program
does not need to copy some of its parts when loading it.
| -rw-r--r-- | lbaselib.c | 15 | ||||
| -rw-r--r-- | ldo.c | 13 | ||||
| -rw-r--r-- | lfunc.c | 6 | ||||
| -rw-r--r-- | lobject.h | 1 | ||||
| -rw-r--r-- | lstring.c | 2 | ||||
| -rw-r--r-- | ltests.c | 7 | ||||
| -rw-r--r-- | lundump.c | 42 | ||||
| -rw-r--r-- | lundump.h | 3 | ||||
| -rw-r--r-- | lzio.c | 36 | ||||
| -rw-r--r-- | lzio.h | 1 | ||||
| -rw-r--r-- | manual/manual.of | 17 | ||||
| -rw-r--r-- | testes/api.lua | 51 |
12 files changed, 160 insertions, 34 deletions
| @@ -337,9 +337,20 @@ static int load_aux (lua_State *L, int status, int envidx) { | |||
| 337 | } | 337 | } |
| 338 | 338 | ||
| 339 | 339 | ||
| 340 | static const char *getmode (lua_State *L, int idx) { | ||
| 341 | const char *mode = luaL_optstring(L, idx, "bt"); | ||
| 342 | int i = 0; | ||
| 343 | if (mode[i] == 'b') i++; | ||
| 344 | if (mode[i] == 't') i++; | ||
| 345 | if (mode[i] != '\0') | ||
| 346 | luaL_argerror(L, idx, "invalid mode"); | ||
| 347 | return mode; | ||
| 348 | } | ||
| 349 | |||
| 350 | |||
| 340 | static int luaB_loadfile (lua_State *L) { | 351 | static int luaB_loadfile (lua_State *L) { |
| 341 | const char *fname = luaL_optstring(L, 1, NULL); | 352 | const char *fname = luaL_optstring(L, 1, NULL); |
| 342 | const char *mode = luaL_optstring(L, 2, NULL); | 353 | const char *mode = getmode(L, 2); |
| 343 | int env = (!lua_isnone(L, 3) ? 3 : 0); /* 'env' index or 0 if no 'env' */ | 354 | int env = (!lua_isnone(L, 3) ? 3 : 0); /* 'env' index or 0 if no 'env' */ |
| 344 | int status = luaL_loadfilex(L, fname, mode); | 355 | int status = luaL_loadfilex(L, fname, mode); |
| 345 | return load_aux(L, status, env); | 356 | return load_aux(L, status, env); |
| @@ -388,7 +399,7 @@ static int luaB_load (lua_State *L) { | |||
| 388 | int status; | 399 | int status; |
| 389 | size_t l; | 400 | size_t l; |
| 390 | const char *s = lua_tolstring(L, 1, &l); | 401 | const char *s = lua_tolstring(L, 1, &l); |
| 391 | const char *mode = luaL_optstring(L, 3, "bt"); | 402 | const char *mode = getmode(L, 3); |
| 392 | int env = (!lua_isnone(L, 4) ? 4 : 0); /* 'env' index or 0 if no 'env' */ | 403 | int env = (!lua_isnone(L, 4) ? 4 : 0); /* 'env' index or 0 if no 'env' */ |
| 393 | if (s != NULL) { /* loading a string? */ | 404 | if (s != NULL) { /* loading a string? */ |
| 394 | const char *chunkname = luaL_optstring(L, 2, s); | 405 | const char *chunkname = luaL_optstring(L, 2, s); |
| @@ -977,7 +977,7 @@ struct SParser { /* data to 'f_parser' */ | |||
| 977 | 977 | ||
| 978 | 978 | ||
| 979 | static void checkmode (lua_State *L, const char *mode, const char *x) { | 979 | static void checkmode (lua_State *L, const char *mode, const char *x) { |
| 980 | if (mode && strchr(mode, x[0]) == NULL) { | 980 | if (strchr(mode, x[0]) == NULL) { |
| 981 | luaO_pushfstring(L, | 981 | luaO_pushfstring(L, |
| 982 | "attempt to load a %s chunk (mode is '%s')", x, mode); | 982 | "attempt to load a %s chunk (mode is '%s')", x, mode); |
| 983 | luaD_throw(L, LUA_ERRSYNTAX); | 983 | luaD_throw(L, LUA_ERRSYNTAX); |
| @@ -988,13 +988,18 @@ static void checkmode (lua_State *L, const char *mode, const char *x) { | |||
| 988 | static void f_parser (lua_State *L, void *ud) { | 988 | static void f_parser (lua_State *L, void *ud) { |
| 989 | LClosure *cl; | 989 | LClosure *cl; |
| 990 | struct SParser *p = cast(struct SParser *, ud); | 990 | struct SParser *p = cast(struct SParser *, ud); |
| 991 | const char *mode = p->mode ? p->mode : "bt"; | ||
| 991 | int c = zgetc(p->z); /* read first character */ | 992 | int c = zgetc(p->z); /* read first character */ |
| 992 | if (c == LUA_SIGNATURE[0]) { | 993 | if (c == LUA_SIGNATURE[0]) { |
| 993 | checkmode(L, p->mode, "binary"); | 994 | int fixed = 0; |
| 994 | cl = luaU_undump(L, p->z, p->name); | 995 | if (strchr(mode, 'B') != NULL) |
| 996 | fixed = 1; | ||
| 997 | else | ||
| 998 | checkmode(L, mode, "binary"); | ||
| 999 | cl = luaU_undump(L, p->z, p->name, fixed); | ||
| 995 | } | 1000 | } |
| 996 | else { | 1001 | else { |
| 997 | checkmode(L, p->mode, "text"); | 1002 | checkmode(L, mode, "text"); |
| 998 | cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c); | 1003 | cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c); |
| 999 | } | 1004 | } |
| 1000 | lua_assert(cl->nupvalues == cl->p->sizeupvalues); | 1005 | lua_assert(cl->nupvalues == cl->p->sizeupvalues); |
| @@ -265,10 +265,12 @@ Proto *luaF_newproto (lua_State *L) { | |||
| 265 | 265 | ||
| 266 | 266 | ||
| 267 | void luaF_freeproto (lua_State *L, Proto *f) { | 267 | void luaF_freeproto (lua_State *L, Proto *f) { |
| 268 | luaM_freearray(L, f->code, f->sizecode); | 268 | if (!(f->flag & PF_FIXED)) { |
| 269 | luaM_freearray(L, f->code, f->sizecode); | ||
| 270 | luaM_freearray(L, f->lineinfo, f->sizelineinfo); | ||
| 271 | } | ||
| 269 | luaM_freearray(L, f->p, f->sizep); | 272 | luaM_freearray(L, f->p, f->sizep); |
| 270 | luaM_freearray(L, f->k, f->sizek); | 273 | luaM_freearray(L, f->k, f->sizek); |
| 271 | luaM_freearray(L, f->lineinfo, f->sizelineinfo); | ||
| 272 | luaM_freearray(L, f->abslineinfo, f->sizeabslineinfo); | 274 | luaM_freearray(L, f->abslineinfo, f->sizeabslineinfo); |
| 273 | luaM_freearray(L, f->locvars, f->sizelocvars); | 275 | luaM_freearray(L, f->locvars, f->sizelocvars); |
| 274 | luaM_freearray(L, f->upvalues, f->sizeupvalues); | 276 | luaM_freearray(L, f->upvalues, f->sizeupvalues); |
| @@ -556,6 +556,7 @@ typedef struct AbsLineInfo { | |||
| 556 | ** Flags in Prototypes | 556 | ** Flags in Prototypes |
| 557 | */ | 557 | */ |
| 558 | #define PF_ISVARARG 1 | 558 | #define PF_ISVARARG 1 |
| 559 | #define PF_FIXED 2 /* prototype has parts in fixed memory */ | ||
| 559 | 560 | ||
| 560 | 561 | ||
| 561 | /* | 562 | /* |
| @@ -207,8 +207,8 @@ static TString *internshrstr (lua_State *L, const char *str, size_t l) { | |||
| 207 | list = &tb->hash[lmod(h, tb->size)]; /* rehash with new size */ | 207 | list = &tb->hash[lmod(h, tb->size)]; /* rehash with new size */ |
| 208 | } | 208 | } |
| 209 | ts = createstrobj(L, l, LUA_VSHRSTR, h); | 209 | ts = createstrobj(L, l, LUA_VSHRSTR, h); |
| 210 | memcpy(getshrstr(ts), str, l * sizeof(char)); | ||
| 211 | ts->shrlen = cast_byte(l); | 210 | ts->shrlen = cast_byte(l); |
| 211 | memcpy(getshrstr(ts), str, l * sizeof(char)); | ||
| 212 | ts->u.hnext = *list; | 212 | ts->u.hnext = *list; |
| 213 | *list = ts; | 213 | *list = ts; |
| 214 | tb->nuse++; | 214 | tb->nuse++; |
| @@ -1513,8 +1513,11 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) { | |||
| 1513 | luaL_loadfile(L1, luaL_checkstring(L1, getnum)); | 1513 | luaL_loadfile(L1, luaL_checkstring(L1, getnum)); |
| 1514 | } | 1514 | } |
| 1515 | else if EQ("loadstring") { | 1515 | else if EQ("loadstring") { |
| 1516 | const char *s = luaL_checkstring(L1, getnum); | 1516 | size_t slen; |
| 1517 | luaL_loadstring(L1, s); | 1517 | const char *s = luaL_checklstring(L1, getnum, &slen); |
| 1518 | const char *name = getstring; | ||
| 1519 | const char *mode = getstring; | ||
| 1520 | luaL_loadbufferx(L1, s, slen, name, mode); | ||
| 1518 | } | 1521 | } |
| 1519 | else if EQ("newmetatable") { | 1522 | else if EQ("newmetatable") { |
| 1520 | lua_pushboolean(L1, luaL_newmetatable(L1, getstring)); | 1523 | lua_pushboolean(L1, luaL_newmetatable(L1, getstring)); |
| @@ -38,6 +38,7 @@ typedef struct { | |||
| 38 | Table *h; /* list for string reuse */ | 38 | Table *h; /* list for string reuse */ |
| 39 | lu_mem offset; /* current position relative to beginning of dump */ | 39 | lu_mem offset; /* current position relative to beginning of dump */ |
| 40 | lua_Integer nstr; /* number of strings in the list */ | 40 | lua_Integer nstr; /* number of strings in the list */ |
| 41 | lu_byte fixed; /* dump is fixed in memory */ | ||
| 41 | } LoadState; | 42 | } LoadState; |
| 42 | 43 | ||
| 43 | 44 | ||
| @@ -70,6 +71,16 @@ static void loadAlign (LoadState *S, int align) { | |||
| 70 | } | 71 | } |
| 71 | 72 | ||
| 72 | 73 | ||
| 74 | #define getaddr(S,n,t) cast(t *, getaddr_(S,n,sizeof(t))) | ||
| 75 | |||
| 76 | static const void *getaddr_ (LoadState *S, int n, int sz) { | ||
| 77 | const void *block = luaZ_getaddr(S->Z, n * sz); | ||
| 78 | if (block == NULL) | ||
| 79 | error(S, "truncated fixed buffer"); | ||
| 80 | return block; | ||
| 81 | } | ||
| 82 | |||
| 83 | |||
| 73 | #define loadVar(S,x) loadVector(S,&x,1) | 84 | #define loadVar(S,x) loadVector(S,&x,1) |
| 74 | 85 | ||
| 75 | 86 | ||
| @@ -169,10 +180,16 @@ static TString *loadString (LoadState *S, Proto *p) { | |||
| 169 | 180 | ||
| 170 | static void loadCode (LoadState *S, Proto *f) { | 181 | static void loadCode (LoadState *S, Proto *f) { |
| 171 | int n = loadInt(S); | 182 | int n = loadInt(S); |
| 172 | f->code = luaM_newvectorchecked(S->L, n, Instruction); | ||
| 173 | f->sizecode = n; | ||
| 174 | loadAlign(S, sizeof(f->code[0])); | 183 | loadAlign(S, sizeof(f->code[0])); |
| 175 | loadVector(S, f->code, n); | 184 | if (S->fixed) { |
| 185 | f->code = getaddr(S, n, Instruction); | ||
| 186 | f->sizecode = n; | ||
| 187 | } | ||
| 188 | else { | ||
| 189 | f->code = luaM_newvectorchecked(S->L, n, Instruction); | ||
| 190 | f->sizecode = n; | ||
| 191 | loadVector(S, f->code, n); | ||
| 192 | } | ||
| 176 | } | 193 | } |
| 177 | 194 | ||
| 178 | 195 | ||
| @@ -254,9 +271,15 @@ static void loadUpvalues (LoadState *S, Proto *f) { | |||
| 254 | static void loadDebug (LoadState *S, Proto *f) { | 271 | static void loadDebug (LoadState *S, Proto *f) { |
| 255 | int i, n; | 272 | int i, n; |
| 256 | n = loadInt(S); | 273 | n = loadInt(S); |
| 257 | f->lineinfo = luaM_newvectorchecked(S->L, n, ls_byte); | 274 | if (S->fixed) { |
| 258 | f->sizelineinfo = n; | 275 | f->lineinfo = getaddr(S, n, ls_byte); |
| 259 | loadVector(S, f->lineinfo, n); | 276 | f->sizelineinfo = n; |
| 277 | } | ||
| 278 | else { | ||
| 279 | f->lineinfo = luaM_newvectorchecked(S->L, n, ls_byte); | ||
| 280 | f->sizelineinfo = n; | ||
| 281 | loadVector(S, f->lineinfo, n); | ||
| 282 | } | ||
| 260 | n = loadInt(S); | 283 | n = loadInt(S); |
| 261 | f->abslineinfo = luaM_newvectorchecked(S->L, n, AbsLineInfo); | 284 | f->abslineinfo = luaM_newvectorchecked(S->L, n, AbsLineInfo); |
| 262 | f->sizeabslineinfo = n; | 285 | f->sizeabslineinfo = n; |
| @@ -287,7 +310,9 @@ static void loadFunction (LoadState *S, Proto *f) { | |||
| 287 | f->linedefined = loadInt(S); | 310 | f->linedefined = loadInt(S); |
| 288 | f->lastlinedefined = loadInt(S); | 311 | f->lastlinedefined = loadInt(S); |
| 289 | f->numparams = loadByte(S); | 312 | f->numparams = loadByte(S); |
| 290 | f->flag = loadByte(S) & PF_ISVARARG; /* keep only the meaningful flags */ | 313 | f->flag = loadByte(S) & PF_ISVARARG; /* get only the meaningful flags */ |
| 314 | if (S->fixed) | ||
| 315 | f->flag |= PF_FIXED; /* signal that code is fixed */ | ||
| 291 | f->maxstacksize = loadByte(S); | 316 | f->maxstacksize = loadByte(S); |
| 292 | loadCode(S, f); | 317 | loadCode(S, f); |
| 293 | loadConstants(S, f); | 318 | loadConstants(S, f); |
| @@ -335,7 +360,7 @@ static void checkHeader (LoadState *S) { | |||
| 335 | /* | 360 | /* |
| 336 | ** Load precompiled chunk. | 361 | ** Load precompiled chunk. |
| 337 | */ | 362 | */ |
| 338 | LClosure *luaU_undump (lua_State *L, ZIO *Z, const char *name) { | 363 | LClosure *luaU_undump (lua_State *L, ZIO *Z, const char *name, int fixed) { |
| 339 | LoadState S; | 364 | LoadState S; |
| 340 | LClosure *cl; | 365 | LClosure *cl; |
| 341 | if (*name == '@' || *name == '=') | 366 | if (*name == '@' || *name == '=') |
| @@ -346,6 +371,7 @@ LClosure *luaU_undump (lua_State *L, ZIO *Z, const char *name) { | |||
| 346 | S.name = name; | 371 | S.name = name; |
| 347 | S.L = L; | 372 | S.L = L; |
| 348 | S.Z = Z; | 373 | S.Z = Z; |
| 374 | S.fixed = fixed; | ||
| 349 | S.offset = 1; /* fist byte was already read */ | 375 | S.offset = 1; /* fist byte was already read */ |
| 350 | checkHeader(&S); | 376 | checkHeader(&S); |
| 351 | cl = luaF_newLclosure(L, loadByte(&S)); | 377 | cl = luaF_newLclosure(L, loadByte(&S)); |
| @@ -26,7 +26,8 @@ | |||
| 26 | #define LUAC_FORMAT 0 /* this is the official format */ | 26 | #define LUAC_FORMAT 0 /* this is the official format */ |
| 27 | 27 | ||
| 28 | /* load one chunk; from lundump.c */ | 28 | /* load one chunk; from lundump.c */ |
| 29 | LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name); | 29 | LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name, |
| 30 | int fixed); | ||
| 30 | 31 | ||
| 31 | /* dump one chunk; from ldump.c */ | 32 | /* dump one chunk; from ldump.c */ |
| 32 | LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, | 33 | LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, |
| @@ -45,17 +45,25 @@ void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { | |||
| 45 | 45 | ||
| 46 | 46 | ||
| 47 | /* --------------------------------------------------------------- read --- */ | 47 | /* --------------------------------------------------------------- read --- */ |
| 48 | |||
| 49 | static int checkbuffer (ZIO *z) { | ||
| 50 | if (z->n == 0) { /* no bytes in buffer? */ | ||
| 51 | if (luaZ_fill(z) == EOZ) /* try to read more */ | ||
| 52 | return 0; /* no more input */ | ||
| 53 | else { | ||
| 54 | z->n++; /* luaZ_fill consumed first byte; put it back */ | ||
| 55 | z->p--; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | return 1; /* now buffer has something */ | ||
| 59 | } | ||
| 60 | |||
| 61 | |||
| 48 | size_t luaZ_read (ZIO *z, void *b, size_t n) { | 62 | size_t luaZ_read (ZIO *z, void *b, size_t n) { |
| 49 | while (n) { | 63 | while (n) { |
| 50 | size_t m; | 64 | size_t m; |
| 51 | if (z->n == 0) { /* no bytes in buffer? */ | 65 | if (!checkbuffer(z)) |
| 52 | if (luaZ_fill(z) == EOZ) /* try to read more */ | 66 | return n; /* no more input; return number of missing bytes */ |
| 53 | return n; /* no more input; return number of missing bytes */ | ||
| 54 | else { | ||
| 55 | z->n++; /* luaZ_fill consumed first byte; put it back */ | ||
| 56 | z->p--; | ||
| 57 | } | ||
| 58 | } | ||
| 59 | m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ | 67 | m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ |
| 60 | memcpy(b, z->p, m); | 68 | memcpy(b, z->p, m); |
| 61 | z->n -= m; | 69 | z->n -= m; |
| @@ -66,3 +74,15 @@ size_t luaZ_read (ZIO *z, void *b, size_t n) { | |||
| 66 | return 0; | 74 | return 0; |
| 67 | } | 75 | } |
| 68 | 76 | ||
| 77 | |||
| 78 | const void *luaZ_getaddr (ZIO* z, size_t n) { | ||
| 79 | const void *res; | ||
| 80 | if (!checkbuffer(z)) | ||
| 81 | return NULL; /* no more input */ | ||
| 82 | if (z->n < n) /* not enough bytes? */ | ||
| 83 | return NULL; /* block not whole; cannot give an address */ | ||
| 84 | res = z->p; /* get block address */ | ||
| 85 | z->n -= n; /* consume these bytes */ | ||
| 86 | z->p += n; | ||
| 87 | return res; | ||
| 88 | } | ||
| @@ -48,6 +48,7 @@ LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, | |||
| 48 | void *data); | 48 | void *data); |
| 49 | LUAI_FUNC size_t luaZ_read (ZIO* z, void *b, size_t n); /* read next n bytes */ | 49 | LUAI_FUNC size_t luaZ_read (ZIO* z, void *b, size_t n); /* read next n bytes */ |
| 50 | 50 | ||
| 51 | LUAI_FUNC const void *luaZ_getaddr (ZIO* z, size_t n); | ||
| 51 | 52 | ||
| 52 | 53 | ||
| 53 | /* --------- Private Part ------------------ */ | 54 | /* --------- Private Part ------------------ */ |
diff --git a/manual/manual.of b/manual/manual.of index c16039b4..3eab69fa 100644 --- a/manual/manual.of +++ b/manual/manual.of | |||
| @@ -2730,7 +2730,8 @@ For such errors, Lua does not call the @x{message handler}. | |||
| 2730 | 2730 | ||
| 2731 | @item{@defid{LUA_ERRERR}| error while running the @x{message handler}.} | 2731 | @item{@defid{LUA_ERRERR}| error while running the @x{message handler}.} |
| 2732 | 2732 | ||
| 2733 | @item{@defid{LUA_ERRSYNTAX}| syntax error during precompilation.} | 2733 | @item{@defid{LUA_ERRSYNTAX}| syntax error during precompilation |
| 2734 | or format error in a binary chunk.} | ||
| 2734 | 2735 | ||
| 2735 | @item{@defid{LUA_YIELD}| the thread (coroutine) yields.} | 2736 | @item{@defid{LUA_YIELD}| the thread (coroutine) yields.} |
| 2736 | 2737 | ||
| @@ -3646,6 +3647,18 @@ and loads it accordingly (see program @idx{luac}). | |||
| 3646 | The string @id{mode} works as in function @Lid{load}, | 3647 | The string @id{mode} works as in function @Lid{load}, |
| 3647 | with the addition that | 3648 | with the addition that |
| 3648 | a @id{NULL} value is equivalent to the string @St{bt}. | 3649 | a @id{NULL} value is equivalent to the string @St{bt}. |
| 3650 | Moreover, it may have a @Char{B} instead of a @Char{b}, | ||
| 3651 | meaning a @emphx{fixed buffer} with the binary dump. | ||
| 3652 | |||
| 3653 | A fixed buffer means that the address returned by the reader function | ||
| 3654 | should contain the chunk until everything created by the chunk has | ||
| 3655 | been collected. | ||
| 3656 | (In general, a fixed buffer would keep the chunk | ||
| 3657 | as its contents until the end of the program, | ||
| 3658 | for instance with the chunk in ROM.) | ||
| 3659 | Moreover, for a fixed buffer, | ||
| 3660 | the reader function should return the entire chunk in the first read. | ||
| 3661 | (As an example, @Lid{luaL_loadbufferx} does that.) | ||
| 3649 | 3662 | ||
| 3650 | @id{lua_load} uses the stack internally, | 3663 | @id{lua_load} uses the stack internally, |
| 3651 | so the reader function must always leave the stack | 3664 | so the reader function must always leave the stack |
| @@ -5688,6 +5701,8 @@ This function returns the same results as @Lid{lua_load}. | |||
| 5688 | @id{name} is the chunk name, | 5701 | @id{name} is the chunk name, |
| 5689 | used for debug information and error messages. | 5702 | used for debug information and error messages. |
| 5690 | The string @id{mode} works as in the function @Lid{lua_load}. | 5703 | The string @id{mode} works as in the function @Lid{lua_load}. |
| 5704 | In particular, this function supports mode @Char{B} for | ||
| 5705 | fixed buffers. | ||
| 5691 | 5706 | ||
| 5692 | } | 5707 | } |
| 5693 | 5708 | ||
diff --git a/testes/api.lua b/testes/api.lua index dece98f5..181c1d53 100644 --- a/testes/api.lua +++ b/testes/api.lua | |||
| @@ -407,7 +407,7 @@ do | |||
| 407 | concat 3]]) == "hi alo mundo") | 407 | concat 3]]) == "hi alo mundo") |
| 408 | 408 | ||
| 409 | -- "argerror" without frames | 409 | -- "argerror" without frames |
| 410 | assert(T.checkpanic("loadstring 4") == | 410 | assert(T.checkpanic("loadstring 4 name bt") == |
| 411 | "bad argument #4 (string expected, got no value)") | 411 | "bad argument #4 (string expected, got no value)") |
| 412 | 412 | ||
| 413 | 413 | ||
| @@ -420,7 +420,7 @@ do | |||
| 420 | if not _soft then | 420 | if not _soft then |
| 421 | local msg = T.checkpanic[[ | 421 | local msg = T.checkpanic[[ |
| 422 | pushstring "function f() f() end" | 422 | pushstring "function f() f() end" |
| 423 | loadstring -1; call 0 0 | 423 | loadstring -1 name t; call 0 0 |
| 424 | getglobal f; call 0 0 | 424 | getglobal f; call 0 0 |
| 425 | ]] | 425 | ]] |
| 426 | assert(string.find(msg, "stack overflow")) | 426 | assert(string.find(msg, "stack overflow")) |
| @@ -430,7 +430,7 @@ do | |||
| 430 | assert(T.checkpanic([[ | 430 | assert(T.checkpanic([[ |
| 431 | pushstring "return {__close = function () Y = 'ho'; end}" | 431 | pushstring "return {__close = function () Y = 'ho'; end}" |
| 432 | newtable | 432 | newtable |
| 433 | loadstring -2 | 433 | loadstring -2 name t |
| 434 | call 0 1 | 434 | call 0 1 |
| 435 | setmetatable -2 | 435 | setmetatable -2 |
| 436 | toclose -1 | 436 | toclose -1 |
| @@ -458,6 +458,8 @@ if not _soft then | |||
| 458 | print'+' | 458 | print'+' |
| 459 | end | 459 | end |
| 460 | 460 | ||
| 461 | |||
| 462 | |||
| 461 | local lim = _soft and 500 or 12000 | 463 | local lim = _soft and 500 or 12000 |
| 462 | local prog = {"checkstack " .. (lim * 2 + 100) .. "msg", "newtable"} | 464 | local prog = {"checkstack " .. (lim * 2 + 100) .. "msg", "newtable"} |
| 463 | for i = 1,lim do | 465 | for i = 1,lim do |
| @@ -481,10 +483,20 @@ for i = 1,lim do assert(t[i] == i*10); t[i] = undef end | |||
| 481 | assert(next(t) == nil) | 483 | assert(next(t) == nil) |
| 482 | prog, g, t = nil | 484 | prog, g, t = nil |
| 483 | 485 | ||
| 486 | do -- shrink stack | ||
| 487 | local m1, m2 = 0, collectgarbage"count" * 1024 | ||
| 488 | while m1 ~= m2 do -- repeat until stable | ||
| 489 | collectgarbage() | ||
| 490 | m1 = m2 | ||
| 491 | m2 = collectgarbage"count" * 1024 | ||
| 492 | end | ||
| 493 | end | ||
| 494 | |||
| 495 | |||
| 484 | -- testing errors | 496 | -- testing errors |
| 485 | 497 | ||
| 486 | a = T.testC([[ | 498 | a = T.testC([[ |
| 487 | loadstring 2; pcall 0 1 0; | 499 | loadstring 2 name t; pcall 0 1 0; |
| 488 | pushvalue 3; insert -2; pcall 1 1 0; | 500 | pushvalue 3; insert -2; pcall 1 1 0; |
| 489 | pcall 0 0 0; | 501 | pcall 0 0 0; |
| 490 | return 1 | 502 | return 1 |
| @@ -498,7 +510,7 @@ local function check3(p, ...) | |||
| 498 | assert(#arg == 3) | 510 | assert(#arg == 3) |
| 499 | assert(string.find(arg[3], p)) | 511 | assert(string.find(arg[3], p)) |
| 500 | end | 512 | end |
| 501 | check3(":1:", T.testC("loadstring 2; return *", "x=")) | 513 | check3(":1:", T.testC("loadstring 2 name t; return *", "x=")) |
| 502 | check3("%.", T.testC("loadfile 2; return *", ".")) | 514 | check3("%.", T.testC("loadfile 2; return *", ".")) |
| 503 | check3("xxxx", T.testC("loadfile 2; return *", "xxxx")) | 515 | check3("xxxx", T.testC("loadfile 2; return *", "xxxx")) |
| 504 | 516 | ||
| @@ -509,6 +521,35 @@ local function checkerrnopro (code, msg) | |||
| 509 | assert(not stt and string.find(err, msg)) | 521 | assert(not stt and string.find(err, msg)) |
| 510 | end | 522 | end |
| 511 | 523 | ||
| 524 | |||
| 525 | do | ||
| 526 | print("testing load of binaries in fixed buffers") | ||
| 527 | local source = {} | ||
| 528 | local N = 1000 | ||
| 529 | -- create a somewhat "large" source | ||
| 530 | for i = 1, N do source[i] = "X = X + 1; " end | ||
| 531 | source = table.concat(source) | ||
| 532 | -- give chunk an explicit name to avoid using source as name | ||
| 533 | source = load(source, "name1") | ||
| 534 | -- dump without debug information | ||
| 535 | source = string.dump(source, true) | ||
| 536 | -- each "X=X+1" generates 4 opcodes with 4 bytes each | ||
| 537 | assert(#source > N * 4 * 4) | ||
| 538 | collectgarbage(); collectgarbage() | ||
| 539 | local m1 = collectgarbage"count" * 1024 | ||
| 540 | -- load dump using fixed buffer | ||
| 541 | local code = T.testC([[ | ||
| 542 | loadstring 2 name B; | ||
| 543 | return 1 | ||
| 544 | ]], source) | ||
| 545 | collectgarbage() | ||
| 546 | local m2 = collectgarbage"count" * 1024 | ||
| 547 | -- load used fewer than 300 bytes | ||
| 548 | assert(m2 > m1 and m2 - m1 < 300) | ||
| 549 | X = 0; code(); assert(X == N); X = nil | ||
| 550 | end | ||
| 551 | |||
| 552 | |||
| 512 | if not _soft then | 553 | if not _soft then |
| 513 | collectgarbage("stop") -- avoid __gc with full stack | 554 | collectgarbage("stop") -- avoid __gc with full stack |
| 514 | checkerrnopro("pushnum 3; call 0 0", "attempt to call") | 555 | checkerrnopro("pushnum 3; call 0 0", "attempt to call") |
