aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lapi.c15
-rw-r--r--lapi.h15
-rw-r--r--ldo.c32
-rw-r--r--ltests.c3
-rw-r--r--lua.h2
-rw-r--r--testes/api.lua71
6 files changed, 122 insertions, 16 deletions
diff --git a/lapi.c b/lapi.c
index ae6b07ae..4fef43b7 100644
--- a/lapi.c
+++ b/lapi.c
@@ -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
1210LUA_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
1208LUA_API void lua_concat (lua_State *L, int n) { 1219LUA_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);
diff --git a/lapi.h b/lapi.h
index 016f78cc..5a4206f1 100644
--- a/lapi.h
+++ b/lapi.h
@@ -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
diff --git a/ldo.c b/ldo.c
index 78e4c5c3..b7a76ef6 100644
--- a/ldo.c
+++ b/ldo.c
@@ -366,32 +366,38 @@ void luaD_tryfuncTM (lua_State *L, StkId func) {
366** separated. 366** separated.
367*/ 367*/
368static void moveresults (lua_State *L, StkId res, int nres, int wanted) { 368static 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
diff --git a/ltests.c b/ltests.c
index a6968653..fbcb7475 100644
--- a/ltests.c
+++ b/ltests.c
@@ -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;
diff --git a/lua.h b/lua.h
index a014be1f..16d685cc 100644
--- a/lua.h
+++ b/lua.h
@@ -333,6 +333,8 @@ LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s);
333LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); 333LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
334LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); 334LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
335 335
336LUA_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)
967L1 = nil 967L1 = nil
968 968
969print('+') 969print('+')
970-------------------------------------------------------------------------
971-- testing to-be-closed variables
972-------------------------------------------------------------------------
973print"testing to-be-closed variables"
974
975do
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
1039end
1040
970 1041
971------------------------------------------------------------------------- 1042-------------------------------------------------------------------------
972-- testing memory limits 1043-- testing memory limits