diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-10-25 15:30:15 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-10-25 15:30:15 -0300 |
| commit | 34840301b529686ce8168828b140a478a5d44b53 (patch) | |
| tree | daceb3978c3d40fd135fb60e9e60dbff9a47731c | |
| parent | 41c800b352149e037bdebd5f20d2f25ed2a0e2a5 (diff) | |
| download | lua-34840301b529686ce8168828b140a478a5d44b53.tar.gz lua-34840301b529686ce8168828b140a478a5d44b53.tar.bz2 lua-34840301b529686ce8168828b140a478a5d44b53.zip | |
To-be-closed variables in the C API
| -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 |
