diff options
-rw-r--r-- | lapi.c | 15 | ||||
-rw-r--r-- | lapi.h | 15 | ||||
-rw-r--r-- | ldo.c | 32 | ||||
-rw-r--r-- | ltests.c | 3 | ||||
-rw-r--r-- | lua.h | 2 | ||||
-rw-r--r-- | testes/api.lua | 71 |
6 files changed, 122 insertions, 16 deletions
@@ -173,15 +173,17 @@ LUA_API void lua_settop (lua_State *L, int idx) { | |||
173 | StkId func = L->ci->func; | 173 | StkId func = L->ci->func; |
174 | lua_lock(L); | 174 | lua_lock(L); |
175 | if (idx >= 0) { | 175 | if (idx >= 0) { |
176 | StkId newtop = (func + 1) + idx; | ||
176 | api_check(L, idx <= L->stack_last - (func + 1), "new top too large"); | 177 | api_check(L, idx <= L->stack_last - (func + 1), "new top too large"); |
177 | while (L->top < (func + 1) + idx) | 178 | while (L->top < newtop) |
178 | setnilvalue(s2v(L->top++)); | 179 | setnilvalue(s2v(L->top++)); |
179 | L->top = (func + 1) + idx; | 180 | L->top = newtop; |
180 | } | 181 | } |
181 | else { | 182 | else { |
182 | api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top"); | 183 | api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top"); |
183 | L->top += idx+1; /* 'subtract' index (index is negative) */ | 184 | L->top += idx+1; /* 'subtract' index (index is negative) */ |
184 | } | 185 | } |
186 | luaF_close(L, L->top, LUA_OK); | ||
185 | lua_unlock(L); | 187 | lua_unlock(L); |
186 | } | 188 | } |
187 | 189 | ||
@@ -1205,6 +1207,15 @@ LUA_API int lua_next (lua_State *L, int idx) { | |||
1205 | } | 1207 | } |
1206 | 1208 | ||
1207 | 1209 | ||
1210 | LUA_API void lua_tobeclosed (lua_State *L) { | ||
1211 | int nresults = L->ci->nresults; | ||
1212 | luaF_newtbcupval(L, L->top - 1); /* create new to-be-closed upvalue */ | ||
1213 | if (!hastocloseCfunc(nresults)) /* function not marked yet? */ | ||
1214 | L->ci->nresults = codeNresults(nresults); /* mark it */ | ||
1215 | lua_assert(hastocloseCfunc(L->ci->nresults)); | ||
1216 | } | ||
1217 | |||
1218 | |||
1208 | LUA_API void lua_concat (lua_State *L, int n) { | 1219 | LUA_API void lua_concat (lua_State *L, int n) { |
1209 | lua_lock(L); | 1220 | lua_lock(L); |
1210 | api_checknelems(L, n); | 1221 | api_checknelems(L, n); |
@@ -15,10 +15,23 @@ | |||
15 | "stack overflow");} | 15 | "stack overflow");} |
16 | 16 | ||
17 | #define adjustresults(L,nres) \ | 17 | #define adjustresults(L,nres) \ |
18 | { if ((nres) == LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; } | 18 | { if ((nres) <= LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; } |
19 | 19 | ||
20 | #define api_checknelems(L,n) api_check(L, (n) < (L->top - L->ci->func), \ | 20 | #define api_checknelems(L,n) api_check(L, (n) < (L->top - L->ci->func), \ |
21 | "not enough elements in the stack") | 21 | "not enough elements in the stack") |
22 | 22 | ||
23 | 23 | ||
24 | /* | ||
25 | ** To reduce the overhead of returning from C functions, the presence of | ||
26 | ** to-be-closed variables in these functions is coded in the CallInfo's | ||
27 | ** field 'nresults', in a way that functions with no to-be-closed variables | ||
28 | ** with zero, one, or "all" wanted results have no overhead. Functions | ||
29 | ** with other number of wanted results, as well as functions with | ||
30 | ** variables to be closed, have an extra check. | ||
31 | */ | ||
32 | |||
33 | #define hastocloseCfunc(n) ((n) < LUA_MULTRET) | ||
34 | |||
35 | #define codeNresults(n) (-(n) - 3) | ||
36 | |||
24 | #endif | 37 | #endif |
@@ -366,32 +366,38 @@ void luaD_tryfuncTM (lua_State *L, StkId func) { | |||
366 | ** separated. | 366 | ** separated. |
367 | */ | 367 | */ |
368 | static void moveresults (lua_State *L, StkId res, int nres, int wanted) { | 368 | static void moveresults (lua_State *L, StkId res, int nres, int wanted) { |
369 | StkId firstresult; | ||
370 | int i; | ||
369 | switch (wanted) { /* handle typical cases separately */ | 371 | switch (wanted) { /* handle typical cases separately */ |
370 | case 0: /* no values needed */ | 372 | case 0: /* no values needed */ |
371 | L->top = res; | 373 | L->top = res; |
372 | break; | 374 | return; |
373 | case 1: /* one value needed */ | 375 | case 1: /* one value needed */ |
374 | if (nres == 0) /* no results? */ | 376 | if (nres == 0) /* no results? */ |
375 | setnilvalue(s2v(res)); /* adjust with nil */ | 377 | setnilvalue(s2v(res)); /* adjust with nil */ |
376 | else | 378 | else |
377 | setobjs2s(L, res, L->top - nres); /* move it to proper place */ | 379 | setobjs2s(L, res, L->top - nres); /* move it to proper place */ |
378 | L->top = res + 1; | 380 | L->top = res + 1; |
379 | break; | 381 | return; |
380 | case LUA_MULTRET: | 382 | case LUA_MULTRET: |
381 | wanted = nres; /* we want all results */ | 383 | wanted = nres; /* we want all results */ |
382 | /* FALLTHROUGH */ | ||
383 | default: { /* multiple results */ | ||
384 | StkId firstresult = L->top - nres; /* index of first result */ | ||
385 | int i; | ||
386 | /* move all results to correct place */ | ||
387 | for (i = 0; i < nres && i < wanted; i++) | ||
388 | setobjs2s(L, res + i, firstresult + i); | ||
389 | for (; i < wanted; i++) /* complete wanted number of results */ | ||
390 | setnilvalue(s2v(res + i)); | ||
391 | L->top = res + wanted; /* top points after the last result */ | ||
392 | break; | 384 | break; |
393 | } | 385 | default: /* multiple results (or to-be-closed variables) */ |
386 | if (hastocloseCfunc(wanted)) { | ||
387 | luaF_close(L, res, LUA_OK); | ||
388 | wanted = codeNresults(wanted); /* correct value */ | ||
389 | if (wanted == LUA_MULTRET) | ||
390 | wanted = nres; | ||
391 | } | ||
392 | break; | ||
394 | } | 393 | } |
394 | firstresult = L->top - nres; /* index of first result */ | ||
395 | /* move all results to correct place */ | ||
396 | for (i = 0; i < nres && i < wanted; i++) | ||
397 | setobjs2s(L, res + i, firstresult + i); | ||
398 | for (; i < wanted; i++) /* complete wanted number of results */ | ||
399 | setnilvalue(s2v(res + i)); | ||
400 | L->top = res + wanted; /* top points after the last result */ | ||
395 | } | 401 | } |
396 | 402 | ||
397 | 403 | ||
@@ -1554,6 +1554,9 @@ static struct X { int x; } x; | |||
1554 | int i = getindex; | 1554 | int i = getindex; |
1555 | return lua_yieldk(L1, nres, i, Cfunck); | 1555 | return lua_yieldk(L1, nres, i, Cfunck); |
1556 | } | 1556 | } |
1557 | else if EQ("tobeclosed") { | ||
1558 | lua_tobeclosed(L); | ||
1559 | } | ||
1557 | else luaL_error(L, "unknown instruction %s", buff); | 1560 | else luaL_error(L, "unknown instruction %s", buff); |
1558 | } | 1561 | } |
1559 | return 0; | 1562 | return 0; |
@@ -333,6 +333,8 @@ LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s); | |||
333 | LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); | 333 | LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); |
334 | LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); | 334 | LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); |
335 | 335 | ||
336 | LUA_API void (lua_tobeclosed) (lua_State *L); | ||
337 | |||
336 | 338 | ||
337 | /* | 339 | /* |
338 | ** {============================================================== | 340 | ** {============================================================== |
diff --git a/testes/api.lua b/testes/api.lua index 6e11c6ed..988250f7 100644 --- a/testes/api.lua +++ b/testes/api.lua | |||
@@ -967,6 +967,77 @@ T.closestate(L1) | |||
967 | L1 = nil | 967 | L1 = nil |
968 | 968 | ||
969 | print('+') | 969 | print('+') |
970 | ------------------------------------------------------------------------- | ||
971 | -- testing to-be-closed variables | ||
972 | ------------------------------------------------------------------------- | ||
973 | print"testing to-be-closed variables" | ||
974 | |||
975 | do | ||
976 | local openresource = {} | ||
977 | |||
978 | local function newresource () | ||
979 | local x = setmetatable({10}, {__close = function(y) | ||
980 | assert(openresource[#openresource] == y) | ||
981 | openresource[#openresource] = nil | ||
982 | y[1] = y[1] + 1 | ||
983 | end}) | ||
984 | openresource[#openresource + 1] = x | ||
985 | return x | ||
986 | end | ||
987 | |||
988 | local a = T.testC([[ | ||
989 | call 0 1 # create resource | ||
990 | tobeclosed # mark it to be closed | ||
991 | return 1 | ||
992 | ]], newresource) | ||
993 | assert(a[1] == 11) | ||
994 | assert(#openresource == 0) -- was closed | ||
995 | |||
996 | -- repeat the test, but calling function in a 'multret' context | ||
997 | local a = {T.testC([[ | ||
998 | call 0 1 # create resource | ||
999 | tobeclosed # mark it to be closed | ||
1000 | return 2 | ||
1001 | ]], newresource)} | ||
1002 | assert(type(a[1]) == "string" and a[2][1] == 11) | ||
1003 | assert(#openresource == 0) -- was closed | ||
1004 | |||
1005 | -- error | ||
1006 | local a, b = pcall(T.testC, [[ | ||
1007 | call 0 1 # create resource | ||
1008 | tobeclosed # mark it to be closed | ||
1009 | error # resource is the error object | ||
1010 | ]], newresource) | ||
1011 | assert(a == false and b[1] == 11) | ||
1012 | assert(#openresource == 0) -- was closed | ||
1013 | |||
1014 | local function check (n) | ||
1015 | assert(#openresource == n) | ||
1016 | end | ||
1017 | |||
1018 | -- closing resources with 'settop' | ||
1019 | local a = T.testC([[ | ||
1020 | pushvalue 2 | ||
1021 | call 0 1 # create resource | ||
1022 | tobeclosed # mark it to be closed | ||
1023 | pushvalue 2 | ||
1024 | call 0 1 # create another resource | ||
1025 | tobeclosed # mark it to be closed | ||
1026 | pushvalue 3 | ||
1027 | pushint 2 # there should be two open resources | ||
1028 | call 1 0 | ||
1029 | pop 1 # pop second resource from the stack | ||
1030 | pushvalue 3 | ||
1031 | pushint 1 # there should be one open resource | ||
1032 | call 1 0 | ||
1033 | pop 1 # pop second resource from the stack | ||
1034 | pushint * | ||
1035 | return 1 # return stack size | ||
1036 | ]], newresource, check) | ||
1037 | assert(a == 3) -- no extra items left in the stack | ||
1038 | |||
1039 | end | ||
1040 | |||
970 | 1041 | ||
971 | ------------------------------------------------------------------------- | 1042 | ------------------------------------------------------------------------- |
972 | -- testing memory limits | 1043 | -- testing memory limits |