diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-11-29 16:02:44 -0200 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-11-29 16:02:44 -0200 |
commit | 6d04537ea660fd12fc16c328366b701fabaf4919 (patch) | |
tree | 41eab6e4d87552e29731db552f7d58d679c56973 | |
parent | 7696c6474fe51ed59fee324e78c1233af74febdd (diff) | |
download | lua-6d04537ea660fd12fc16c328366b701fabaf4919.tar.gz lua-6d04537ea660fd12fc16c328366b701fabaf4919.tar.bz2 lua-6d04537ea660fd12fc16c328366b701fabaf4919.zip |
A to-be-closed variable must have a closable value (or be nil)
It is an error for a to-be-closed variable to have a non-closable
non-nil value when it is being closed. This situation does not seem to
be useful and often hints to an error. (Particularly in the C API, it is
easy to change a to-be-closed index by mistake.)
-rw-r--r-- | lapi.c | 2 | ||||
-rw-r--r-- | ldebug.c | 18 | ||||
-rw-r--r-- | ldebug.h | 2 | ||||
-rw-r--r-- | lfunc.c | 6 | ||||
-rw-r--r-- | lvm.c | 7 | ||||
-rw-r--r-- | manual/manual.of | 25 | ||||
-rw-r--r-- | testes/api.lua | 21 | ||||
-rw-r--r-- | testes/db.lua | 20 | ||||
-rw-r--r-- | testes/locals.lua | 21 |
9 files changed, 83 insertions, 39 deletions
@@ -1299,7 +1299,7 @@ static const char *aux_upvalue (TValue *fi, int n, TValue **val, | |||
1299 | *val = f->upvals[n-1]->v; | 1299 | *val = f->upvals[n-1]->v; |
1300 | if (owner) *owner = obj2gco(f->upvals[n - 1]); | 1300 | if (owner) *owner = obj2gco(f->upvals[n - 1]); |
1301 | name = p->upvalues[n-1].name; | 1301 | name = p->upvalues[n-1].name; |
1302 | return (name == NULL) ? "(*no name)" : getstr(name); | 1302 | return (name == NULL) ? "(no name)" : getstr(name); |
1303 | } | 1303 | } |
1304 | default: return NULL; /* not a closure */ | 1304 | default: return NULL; /* not a closure */ |
1305 | } | 1305 | } |
@@ -192,15 +192,14 @@ static const char *findvararg (CallInfo *ci, int n, StkId *pos) { | |||
192 | int nextra = ci->u.l.nextraargs; | 192 | int nextra = ci->u.l.nextraargs; |
193 | if (n <= nextra) { | 193 | if (n <= nextra) { |
194 | *pos = ci->func - nextra + (n - 1); | 194 | *pos = ci->func - nextra + (n - 1); |
195 | return "(*vararg)"; /* generic name for any vararg */ | 195 | return "(vararg)"; /* generic name for any vararg */ |
196 | } | 196 | } |
197 | } | 197 | } |
198 | return NULL; /* no such vararg */ | 198 | return NULL; /* no such vararg */ |
199 | } | 199 | } |
200 | 200 | ||
201 | 201 | ||
202 | static const char *findlocal (lua_State *L, CallInfo *ci, int n, | 202 | const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) { |
203 | StkId *pos) { | ||
204 | StkId base = ci->func + 1; | 203 | StkId base = ci->func + 1; |
205 | const char *name = NULL; | 204 | const char *name = NULL; |
206 | if (isLua(ci)) { | 205 | if (isLua(ci)) { |
@@ -211,12 +210,15 @@ static const char *findlocal (lua_State *L, CallInfo *ci, int n, | |||
211 | } | 210 | } |
212 | if (name == NULL) { /* no 'standard' name? */ | 211 | if (name == NULL) { /* no 'standard' name? */ |
213 | StkId limit = (ci == L->ci) ? L->top : ci->next->func; | 212 | StkId limit = (ci == L->ci) ? L->top : ci->next->func; |
214 | if (limit - base >= n && n > 0) /* is 'n' inside 'ci' stack? */ | 213 | if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */ |
215 | name = "(*temporary)"; /* generic name for any valid slot */ | 214 | /* generic name for any valid slot */ |
215 | name = isLua(ci) ? "(temporary)" : "(C temporary)"; | ||
216 | } | ||
216 | else | 217 | else |
217 | return NULL; /* no name */ | 218 | return NULL; /* no name */ |
218 | } | 219 | } |
219 | *pos = base + (n - 1); | 220 | if (pos) |
221 | *pos = base + (n - 1); | ||
220 | return name; | 222 | return name; |
221 | } | 223 | } |
222 | 224 | ||
@@ -232,7 +234,7 @@ LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { | |||
232 | } | 234 | } |
233 | else { /* active function; get information through 'ar' */ | 235 | else { /* active function; get information through 'ar' */ |
234 | StkId pos = NULL; /* to avoid warnings */ | 236 | StkId pos = NULL; /* to avoid warnings */ |
235 | name = findlocal(L, ar->i_ci, n, &pos); | 237 | name = luaG_findlocal(L, ar->i_ci, n, &pos); |
236 | if (name) { | 238 | if (name) { |
237 | setobjs2s(L, L->top, pos); | 239 | setobjs2s(L, L->top, pos); |
238 | api_incr_top(L); | 240 | api_incr_top(L); |
@@ -247,7 +249,7 @@ LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { | |||
247 | StkId pos = NULL; /* to avoid warnings */ | 249 | StkId pos = NULL; /* to avoid warnings */ |
248 | const char *name; | 250 | const char *name; |
249 | lua_lock(L); | 251 | lua_lock(L); |
250 | name = findlocal(L, ar->i_ci, n, &pos); | 252 | name = luaG_findlocal(L, ar->i_ci, n, &pos); |
251 | if (name) { | 253 | if (name) { |
252 | setobjs2s(L, pos, L->top - 1); | 254 | setobjs2s(L, pos, L->top - 1); |
253 | L->top--; /* pop value */ | 255 | L->top--; /* pop value */ |
@@ -22,6 +22,8 @@ | |||
22 | #define ABSLINEINFO (-0x80) | 22 | #define ABSLINEINFO (-0x80) |
23 | 23 | ||
24 | LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc); | 24 | LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc); |
25 | LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, | ||
26 | StkId *pos); | ||
25 | LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o, | 27 | LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o, |
26 | const char *opname); | 28 | const char *opname); |
27 | LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o, | 29 | LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o, |
@@ -14,6 +14,7 @@ | |||
14 | 14 | ||
15 | #include "lua.h" | 15 | #include "lua.h" |
16 | 16 | ||
17 | #include "ldebug.h" | ||
17 | #include "ldo.h" | 18 | #include "ldo.h" |
18 | #include "lfunc.h" | 19 | #include "lfunc.h" |
19 | #include "lgc.h" | 20 | #include "lgc.h" |
@@ -140,6 +141,11 @@ static int closeupval (lua_State *L, TValue *uv, StkId level, int status) { | |||
140 | if (likely(status == LUA_OK)) { | 141 | if (likely(status == LUA_OK)) { |
141 | if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */ | 142 | if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */ |
142 | callclose(L, NULL); /* call closing method */ | 143 | callclose(L, NULL); /* call closing method */ |
144 | else if (!ttisnil(uv)) { /* non-closable non-nil value? */ | ||
145 | const char *vname = luaG_findlocal(L, L->ci, level - L->ci->func, NULL); | ||
146 | if (vname == NULL) vname = "?"; | ||
147 | luaG_runerror(L, "attempt to close non-closable variable '%s'", vname); | ||
148 | } | ||
143 | } | 149 | } |
144 | else { /* there was an error */ | 150 | else { /* there was an error */ |
145 | /* save error message and set stack top to 'level + 1' */ | 151 | /* save error message and set stack top to 'level + 1' */ |
@@ -1427,7 +1427,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { | |||
1427 | } | 1427 | } |
1428 | vmcase(OP_CLOSE) { | 1428 | vmcase(OP_CLOSE) { |
1429 | L->top = ra + 1; /* everything is free after this slot */ | 1429 | L->top = ra + 1; /* everything is free after this slot */ |
1430 | ProtectNT(luaF_close(L, ra, LUA_OK)); | 1430 | Protect(luaF_close(L, ra, LUA_OK)); |
1431 | vmbreak; | 1431 | vmbreak; |
1432 | } | 1432 | } |
1433 | vmcase(OP_TBC) { | 1433 | vmcase(OP_TBC) { |
@@ -1717,9 +1717,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) { | |||
1717 | vmbreak; | 1717 | vmbreak; |
1718 | } | 1718 | } |
1719 | vmcase(OP_TFORPREP) { | 1719 | vmcase(OP_TFORPREP) { |
1720 | /* is 'toclose' a function or has a '__close' metamethod? */ | 1720 | /* is 'toclose' not nil? */ |
1721 | if (ttisfunction(s2v(ra + 3)) || | 1721 | if (!ttisnil(s2v(ra + 3))) { |
1722 | !ttisnil(luaT_gettmbyobj(L, s2v(ra + 3), TM_CLOSE))) { | ||
1723 | /* create to-be-closed upvalue for it */ | 1722 | /* create to-be-closed upvalue for it */ |
1724 | halfProtect(luaF_newtbcupval(L, ra + 3)); | 1723 | halfProtect(luaF_newtbcupval(L, ra + 3)); |
1725 | } | 1724 | } |
diff --git a/manual/manual.of b/manual/manual.of index f891c33e..3902f2f3 100644 --- a/manual/manual.of +++ b/manual/manual.of | |||
@@ -1063,11 +1063,16 @@ which start with @T{0x} or @T{0X}. | |||
1063 | Hexadecimal constants also accept an optional fractional part | 1063 | Hexadecimal constants also accept an optional fractional part |
1064 | plus an optional binary exponent, | 1064 | plus an optional binary exponent, |
1065 | marked by a letter @Char{p} or @Char{P}. | 1065 | marked by a letter @Char{p} or @Char{P}. |
1066 | |||
1066 | A numeric constant with a radix point or an exponent | 1067 | A numeric constant with a radix point or an exponent |
1067 | denotes a float; | 1068 | denotes a float; |
1068 | otherwise, | 1069 | otherwise, |
1069 | if its value fits in an integer, | 1070 | if its value fits in an integer or it is a hexadecimal constant, |
1070 | it denotes an integer. | 1071 | it denotes an integer; |
1072 | otherwise (that is, a decimal integer numeral that overflows), | ||
1073 | it denotes a float. | ||
1074 | (Hexadecimal integer numerals that overflow @emph{wrap around}; | ||
1075 | they always denote an integer value.) | ||
1071 | Examples of valid integer constants are | 1076 | Examples of valid integer constants are |
1072 | @verbatim{ | 1077 | @verbatim{ |
1073 | 3 345 0xff 0xBEBADA | 1078 | 3 345 0xff 0xBEBADA |
@@ -1542,7 +1547,8 @@ If the value of the variable when it goes out of scope is a function, | |||
1542 | that function is called; | 1547 | that function is called; |
1543 | otherwise, if the value has a @idx{__close} metamethod, | 1548 | otherwise, if the value has a @idx{__close} metamethod, |
1544 | that metamethod is called; | 1549 | that metamethod is called; |
1545 | otherwise, nothing is done. | 1550 | otherwise, if the value is @nil, nothing is done; |
1551 | otherwise, an error is raised. | ||
1546 | In the function case, | 1552 | In the function case, |
1547 | if the scope is being closed by an error, | 1553 | if the scope is being closed by an error, |
1548 | the error object is passed as an argument to the function; | 1554 | the error object is passed as an argument to the function; |
@@ -1665,7 +1671,7 @@ If both operands are integers, | |||
1665 | the operation is performed over integers and the result is an integer. | 1671 | the operation is performed over integers and the result is an integer. |
1666 | Otherwise, if both operands are numbers, | 1672 | Otherwise, if both operands are numbers, |
1667 | then they are converted to floats, | 1673 | then they are converted to floats, |
1668 | the operation is performed following the usual rules | 1674 | the operation is performed following the machine's rules |
1669 | for floating-point arithmetic | 1675 | for floating-point arithmetic |
1670 | (usually the @x{IEEE 754} standard), | 1676 | (usually the @x{IEEE 754} standard), |
1671 | and the result is a float. | 1677 | and the result is a float. |
@@ -4998,7 +5004,7 @@ This call leaves the final string on the top of the stack. | |||
4998 | 5004 | ||
4999 | } | 5005 | } |
5000 | 5006 | ||
5001 | If you know beforehand the total size of the resulting string, | 5007 | If you know beforehand the maximum size of the resulting string, |
5002 | you can use the buffer like this: | 5008 | you can use the buffer like this: |
5003 | @itemize{ | 5009 | @itemize{ |
5004 | 5010 | ||
@@ -5012,7 +5018,8 @@ size @id{sz} with a call @T{luaL_buffinitsize(L, &b, sz)}.} | |||
5012 | @item{ | 5018 | @item{ |
5013 | Finish by calling @T{luaL_pushresultsize(&b, sz)}, | 5019 | Finish by calling @T{luaL_pushresultsize(&b, sz)}, |
5014 | where @id{sz} is the total size of the resulting string | 5020 | where @id{sz} is the total size of the resulting string |
5015 | copied into that space. | 5021 | copied into that space (which may be smaller than or |
5022 | equal to the preallocated size). | ||
5016 | } | 5023 | } |
5017 | 5024 | ||
5018 | } | 5025 | } |
@@ -5028,8 +5035,8 @@ when you call a buffer operation, | |||
5028 | the stack is at the same level | 5035 | the stack is at the same level |
5029 | it was immediately after the previous buffer operation. | 5036 | it was immediately after the previous buffer operation. |
5030 | (The only exception to this rule is @Lid{luaL_addvalue}.) | 5037 | (The only exception to this rule is @Lid{luaL_addvalue}.) |
5031 | After calling @Lid{luaL_pushresult} the stack is back to its | 5038 | After calling @Lid{luaL_pushresult}, |
5032 | level when the buffer was initialized, | 5039 | the stack is back to its level when the buffer was initialized, |
5033 | plus the final string on its top. | 5040 | plus the final string on its top. |
5034 | 5041 | ||
5035 | } | 5042 | } |
@@ -7118,7 +7125,7 @@ empty string as a match immediately after another match. | |||
7118 | As an example, | 7125 | As an example, |
7119 | consider the results of the following code: | 7126 | consider the results of the following code: |
7120 | @verbatim{ | 7127 | @verbatim{ |
7121 | > string.gsub("abc", "()a*()", print) | 7128 | > string.gsub("abc", "()a*()", print); |
7122 | --> 1 2 | 7129 | --> 1 2 |
7123 | --> 3 3 | 7130 | --> 3 3 |
7124 | --> 4 4 | 7131 | --> 4 4 |
diff --git a/testes/api.lua b/testes/api.lua index ed857fd0..6f35e132 100644 --- a/testes/api.lua +++ b/testes/api.lua | |||
@@ -366,7 +366,7 @@ do | |||
366 | -- "argerror" without frames | 366 | -- "argerror" without frames |
367 | assert(T.checkpanic("loadstring 4") == | 367 | assert(T.checkpanic("loadstring 4") == |
368 | "bad argument #4 (string expected, got no value)") | 368 | "bad argument #4 (string expected, got no value)") |
369 | 369 | ||
370 | 370 | ||
371 | -- memory error | 371 | -- memory error |
372 | T.totalmem(T.totalmem()+10000) -- set low memory limit (+10k) | 372 | T.totalmem(T.totalmem()+10000) -- set low memory limit (+10k) |
@@ -987,12 +987,12 @@ do | |||
987 | 987 | ||
988 | local a, b = T.testC([[ | 988 | local a, b = T.testC([[ |
989 | call 0 1 # create resource | 989 | call 0 1 # create resource |
990 | pushint 34 | 990 | pushnil |
991 | toclose -2 # mark call result to be closed | 991 | toclose -2 # mark call result to be closed |
992 | toclose -1 # mark number to be closed (will be ignored) | 992 | toclose -1 # mark nil to be closed (will be ignored) |
993 | return 2 | 993 | return 2 |
994 | ]], newresource) | 994 | ]], newresource) |
995 | assert(a[1] == 11 and b == 34) | 995 | assert(a[1] == 11 and b == nil) |
996 | assert(#openresource == 0) -- was closed | 996 | assert(#openresource == 0) -- was closed |
997 | 997 | ||
998 | -- repeat the test, but calling function in a 'multret' context | 998 | -- repeat the test, but calling function in a 'multret' context |
@@ -1005,7 +1005,7 @@ do | |||
1005 | assert(#openresource == 0) -- was closed | 1005 | assert(#openresource == 0) -- was closed |
1006 | 1006 | ||
1007 | -- error | 1007 | -- error |
1008 | local a, b = pcall(T.testC, [[ | 1008 | local a, b = pcall(T.makeCfunc[[ |
1009 | call 0 1 # create resource | 1009 | call 0 1 # create resource |
1010 | toclose -1 # mark it to be closed | 1010 | toclose -1 # mark it to be closed |
1011 | error # resource is the error object | 1011 | error # resource is the error object |
@@ -1038,6 +1038,13 @@ do | |||
1038 | ]], newresource, check) | 1038 | ]], newresource, check) |
1039 | assert(a == 3) -- no extra items left in the stack | 1039 | assert(a == 3) -- no extra items left in the stack |
1040 | 1040 | ||
1041 | -- non-closable value | ||
1042 | local a, b = pcall(T.makeCfunc[[ | ||
1043 | pushint 32 | ||
1044 | toclose -1 | ||
1045 | ]]) | ||
1046 | assert(not a and string.find(b, "(C temporary)")) | ||
1047 | |||
1041 | end | 1048 | end |
1042 | 1049 | ||
1043 | 1050 | ||
@@ -1249,9 +1256,9 @@ do -- closing state with no extra memory | |||
1249 | T.closestate(L) | 1256 | T.closestate(L) |
1250 | T.alloccount() | 1257 | T.alloccount() |
1251 | end | 1258 | end |
1252 | 1259 | ||
1253 | do -- garbage collection with no extra memory | 1260 | do -- garbage collection with no extra memory |
1254 | local L = T.newstate() | 1261 | local L = T.newstate() |
1255 | T.loadlib(L) | 1262 | T.loadlib(L) |
1256 | local res = (T.doremote(L, [[ | 1263 | local res = (T.doremote(L, [[ |
1257 | _ENV = require"_G" | 1264 | _ENV = require"_G" |
diff --git a/testes/db.lua b/testes/db.lua index 5b243c39..976962b0 100644 --- a/testes/db.lua +++ b/testes/db.lua | |||
@@ -214,14 +214,14 @@ local function foo (a, ...) | |||
214 | local t = table.pack(...) | 214 | local t = table.pack(...) |
215 | for i = 1, t.n do | 215 | for i = 1, t.n do |
216 | local n, v = debug.getlocal(1, -i) | 216 | local n, v = debug.getlocal(1, -i) |
217 | assert(n == "(*vararg)" and v == t[i]) | 217 | assert(n == "(vararg)" and v == t[i]) |
218 | end | 218 | end |
219 | assert(not debug.getlocal(1, -(t.n + 1))) | 219 | assert(not debug.getlocal(1, -(t.n + 1))) |
220 | assert(not debug.setlocal(1, -(t.n + 1), 30)) | 220 | assert(not debug.setlocal(1, -(t.n + 1), 30)) |
221 | if t.n > 0 then | 221 | if t.n > 0 then |
222 | (function (x) | 222 | (function (x) |
223 | assert(debug.setlocal(2, -1, x) == "(*vararg)") | 223 | assert(debug.setlocal(2, -1, x) == "(vararg)") |
224 | assert(debug.setlocal(2, -t.n, x) == "(*vararg)") | 224 | assert(debug.setlocal(2, -t.n, x) == "(vararg)") |
225 | end)(430) | 225 | end)(430) |
226 | assert(... == 430) | 226 | assert(... == 430) |
227 | end | 227 | end |
@@ -328,9 +328,9 @@ assert(a[f] and a[g] and a[assert] and a[debug.getlocal] and not a[print]) | |||
328 | -- tests for manipulating non-registered locals (C and Lua temporaries) | 328 | -- tests for manipulating non-registered locals (C and Lua temporaries) |
329 | 329 | ||
330 | local n, v = debug.getlocal(0, 1) | 330 | local n, v = debug.getlocal(0, 1) |
331 | assert(v == 0 and n == "(*temporary)") | 331 | assert(v == 0 and n == "(C temporary)") |
332 | local n, v = debug.getlocal(0, 2) | 332 | local n, v = debug.getlocal(0, 2) |
333 | assert(v == 2 and n == "(*temporary)") | 333 | assert(v == 2 and n == "(C temporary)") |
334 | assert(not debug.getlocal(0, 3)) | 334 | assert(not debug.getlocal(0, 3)) |
335 | assert(not debug.getlocal(0, 0)) | 335 | assert(not debug.getlocal(0, 0)) |
336 | 336 | ||
@@ -607,7 +607,7 @@ co = load[[ | |||
607 | local a = 0 | 607 | local a = 0 |
608 | -- 'A' should be visible to debugger only after its complete definition | 608 | -- 'A' should be visible to debugger only after its complete definition |
609 | debug.sethook(function (e, l) | 609 | debug.sethook(function (e, l) |
610 | if l == 3 then a = a + 1; assert(debug.getlocal(2, 1) == "(*temporary)") | 610 | if l == 3 then a = a + 1; assert(debug.getlocal(2, 1) == "(temporary)") |
611 | elseif l == 4 then a = a + 1; assert(debug.getlocal(2, 1) == "A") | 611 | elseif l == 4 then a = a + 1; assert(debug.getlocal(2, 1) == "A") |
612 | end | 612 | end |
613 | end, "l") | 613 | end, "l") |
@@ -875,15 +875,15 @@ local debug = require'debug' | |||
875 | local a = 12 -- a local variable | 875 | local a = 12 -- a local variable |
876 | 876 | ||
877 | local n, v = debug.getlocal(1, 1) | 877 | local n, v = debug.getlocal(1, 1) |
878 | assert(n == "(*temporary)" and v == debug) -- unkown name but known value | 878 | assert(n == "(temporary)" and v == debug) -- unkown name but known value |
879 | n, v = debug.getlocal(1, 2) | 879 | n, v = debug.getlocal(1, 2) |
880 | assert(n == "(*temporary)" and v == 12) -- unkown name but known value | 880 | assert(n == "(temporary)" and v == 12) -- unkown name but known value |
881 | 881 | ||
882 | -- a function with an upvalue | 882 | -- a function with an upvalue |
883 | local f = function () local x; return a end | 883 | local f = function () local x; return a end |
884 | n, v = debug.getupvalue(f, 1) | 884 | n, v = debug.getupvalue(f, 1) |
885 | assert(n == "(*no name)" and v == 12) | 885 | assert(n == "(no name)" and v == 12) |
886 | assert(debug.setupvalue(f, 1, 13) == "(*no name)") | 886 | assert(debug.setupvalue(f, 1, 13) == "(no name)") |
887 | assert(a == 13) | 887 | assert(a == 13) |
888 | 888 | ||
889 | local t = debug.getinfo(f) | 889 | local t = debug.getinfo(f) |
diff --git a/testes/locals.lua b/testes/locals.lua index 90a8b845..24681dd9 100644 --- a/testes/locals.lua +++ b/testes/locals.lua | |||
@@ -266,6 +266,27 @@ do -- errors in __close | |||
266 | end | 266 | end |
267 | 267 | ||
268 | 268 | ||
269 | do | ||
270 | |||
271 | -- errors due to non-closable values | ||
272 | local function foo () | ||
273 | local *toclose x = 34 | ||
274 | end | ||
275 | local stat, msg = pcall(foo) | ||
276 | assert(not stat and string.find(msg, "variable 'x'")) | ||
277 | |||
278 | |||
279 | -- with other errors, non-closable values are ignored | ||
280 | local function foo () | ||
281 | local *toclose x = 34 | ||
282 | local *toclose y = function () error(32) end | ||
283 | end | ||
284 | local stat, msg = pcall(foo) | ||
285 | assert(not stat and msg == 32) | ||
286 | |||
287 | end | ||
288 | |||
289 | |||
269 | if rawget(_G, "T") then | 290 | if rawget(_G, "T") then |
270 | 291 | ||
271 | -- memory error inside closing function | 292 | -- memory error inside closing function |