diff options
| -rw-r--r-- | ldo.c | 4 | ||||
| -rw-r--r-- | lfunc.c | 32 | ||||
| -rw-r--r-- | manual/manual.of | 7 | ||||
| -rw-r--r-- | testes/locals.lua | 44 |
4 files changed, 60 insertions, 27 deletions
| @@ -111,10 +111,6 @@ void luaD_seterrorobj (lua_State *L, TStatus errcode, StkId oldtop) { | |||
| 111 | setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); | 111 | setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); |
| 112 | break; | 112 | break; |
| 113 | } | 113 | } |
| 114 | case LUA_OK: { /* special case only for closing upvalues */ | ||
| 115 | setnilvalue(s2v(oldtop)); /* no error message */ | ||
| 116 | break; | ||
| 117 | } | ||
| 118 | default: { | 114 | default: { |
| 119 | lua_assert(errorstatus(errcode)); /* real error */ | 115 | lua_assert(errorstatus(errcode)); /* real error */ |
| 120 | setobjs2s(L, oldtop, L->top.p - 1); /* error message on current top */ | 116 | setobjs2s(L, oldtop, L->top.p - 1); /* error message on current top */ |
| @@ -100,21 +100,23 @@ UpVal *luaF_findupval (lua_State *L, StkId level) { | |||
| 100 | 100 | ||
| 101 | 101 | ||
| 102 | /* | 102 | /* |
| 103 | ** Call closing method for object 'obj' with error message 'err'. The | 103 | ** Call closing method for object 'obj' with error object 'err'. The |
| 104 | ** boolean 'yy' controls whether the call is yieldable. | 104 | ** boolean 'yy' controls whether the call is yieldable. |
| 105 | ** (This function assumes EXTRA_STACK.) | 105 | ** (This function assumes EXTRA_STACK.) |
| 106 | */ | 106 | */ |
| 107 | static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) { | 107 | static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) { |
| 108 | StkId top = L->top.p; | 108 | StkId top = L->top.p; |
| 109 | StkId func = top; | ||
| 109 | const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); | 110 | const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); |
| 110 | setobj2s(L, top, tm); /* will call metamethod... */ | 111 | setobj2s(L, top++, tm); /* will call metamethod... */ |
| 111 | setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */ | 112 | setobj2s(L, top++, obj); /* with 'self' as the 1st argument */ |
| 112 | setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */ | 113 | if (err != NULL) /* if there was an error... */ |
| 113 | L->top.p = top + 3; /* add function and arguments */ | 114 | setobj2s(L, top++, err); /* then error object will be 2nd argument */ |
| 115 | L->top.p = top; /* add function and arguments */ | ||
| 114 | if (yy) | 116 | if (yy) |
| 115 | luaD_call(L, top, 0); | 117 | luaD_call(L, func, 0); |
| 116 | else | 118 | else |
| 117 | luaD_callnoyield(L, top, 0); | 119 | luaD_callnoyield(L, func, 0); |
| 118 | } | 120 | } |
| 119 | 121 | ||
| 120 | 122 | ||
| @@ -144,11 +146,17 @@ static void prepcallclosemth (lua_State *L, StkId level, TStatus status, | |||
| 144 | int yy) { | 146 | int yy) { |
| 145 | TValue *uv = s2v(level); /* value being closed */ | 147 | TValue *uv = s2v(level); /* value being closed */ |
| 146 | TValue *errobj; | 148 | TValue *errobj; |
| 147 | if (status == CLOSEKTOP) | 149 | switch (status) { |
| 148 | errobj = &G(L)->nilvalue; /* error object is nil */ | 150 | case LUA_OK: |
| 149 | else { /* 'luaD_seterrorobj' will set top to level + 2 */ | 151 | L->top.p = level + 1; /* call will be at this level */ |
| 150 | errobj = s2v(level + 1); /* error object goes after 'uv' */ | 152 | /* FALLTHROUGH */ |
| 151 | luaD_seterrorobj(L, status, level + 1); /* set error object */ | 153 | case CLOSEKTOP: /* don't need to change top */ |
| 154 | errobj = NULL; /* no error object */ | ||
| 155 | break; | ||
| 156 | default: /* 'luaD_seterrorobj' will set top to level + 2 */ | ||
| 157 | errobj = s2v(level + 1); /* error object goes after 'uv' */ | ||
| 158 | luaD_seterrorobj(L, status, level + 1); /* set error object */ | ||
| 159 | break; | ||
| 152 | } | 160 | } |
| 153 | callclosemethod(L, uv, errobj, yy); | 161 | callclosemethod(L, uv, errobj, yy); |
| 154 | } | 162 | } |
diff --git a/manual/manual.of b/manual/manual.of index a55c7b49..ff4e79fe 100644 --- a/manual/manual.of +++ b/manual/manual.of | |||
| @@ -1612,10 +1612,11 @@ or exiting by an error. | |||
| 1612 | Here, to @emph{close} a value means | 1612 | Here, to @emph{close} a value means |
| 1613 | to call its @idx{__close} metamethod. | 1613 | to call its @idx{__close} metamethod. |
| 1614 | When calling the metamethod, | 1614 | When calling the metamethod, |
| 1615 | the value itself is passed as the first argument | 1615 | the value itself is passed as the first argument. |
| 1616 | and the error object that caused the exit (if any) | 1616 | If there was an error, |
| 1617 | the error object that caused the exit | ||
| 1617 | is passed as a second argument; | 1618 | is passed as a second argument; |
| 1618 | if there was no error, the second argument is @nil. | 1619 | otherwise, there is no second argument. |
| 1619 | 1620 | ||
| 1620 | The value assigned to a to-be-closed variable | 1621 | The value assigned to a to-be-closed variable |
| 1621 | must have a @idx{__close} metamethod | 1622 | must have a @idx{__close} metamethod |
diff --git a/testes/locals.lua b/testes/locals.lua index 090d846b..910deb8a 100644 --- a/testes/locals.lua +++ b/testes/locals.lua | |||
| @@ -280,6 +280,32 @@ do | |||
| 280 | end | 280 | end |
| 281 | 281 | ||
| 282 | 282 | ||
| 283 | do -- testing presence of second argument | ||
| 284 | local function foo (howtoclose, obj, n) | ||
| 285 | local ca -- copy of 'a' visible inside its close metamethod | ||
| 286 | do | ||
| 287 | local a <close> = func2close(function (...) | ||
| 288 | local t = table.pack(...) | ||
| 289 | assert(select("#", ...) == n) | ||
| 290 | assert(t.n == n and t[1] == ca and (t.n < 2 or t[2] == obj)) | ||
| 291 | ca = 15 -- final value to be returned if howtoclose=="scope" | ||
| 292 | end) | ||
| 293 | ca = a | ||
| 294 | if howtoclose == "ret" then return obj -- 'a' closed by return | ||
| 295 | elseif howtoclose == "err" then error(obj) -- 'a' closed by error | ||
| 296 | end | ||
| 297 | end -- 'a' closed by end of scope | ||
| 298 | return ca -- ca now should be 15 | ||
| 299 | end | ||
| 300 | -- with no errors, closing methods receive no extra argument | ||
| 301 | assert(foo("scope", nil, 1) == 15) -- close by end of scope | ||
| 302 | assert(foo("ret", 32, 1) == 32) -- close by return | ||
| 303 | -- with errors, they do | ||
| 304 | local st, msg = pcall(foo, "err", 23, 2) -- close by error | ||
| 305 | assert(not st and msg == 23) | ||
| 306 | end | ||
| 307 | |||
| 308 | |||
| 283 | -- testing to-be-closed x compile-time constants | 309 | -- testing to-be-closed x compile-time constants |
| 284 | -- (there were some bugs here in Lua 5.4-rc3, due to a confusion | 310 | -- (there were some bugs here in Lua 5.4-rc3, due to a confusion |
| 285 | -- between compile levels and stack levels of variables) | 311 | -- between compile levels and stack levels of variables) |
| @@ -865,8 +891,10 @@ do | |||
| 865 | if extra then | 891 | if extra then |
| 866 | extrares = co() -- runs until first (extra) yield | 892 | extrares = co() -- runs until first (extra) yield |
| 867 | end | 893 | end |
| 868 | local res = table.pack(co()) -- runs until yield inside '__close' | 894 | local res = table.pack(co()) -- runs until "regular" yield |
| 869 | assert(res.n == 2 and res[2] == nil) | 895 | -- regular yield will yield all values passed to the close function; |
| 896 | -- without errors, that is only the object being closed. | ||
| 897 | assert(res.n == 1 and type(res[1]) == "table") | ||
| 870 | local res2 = table.pack(co()) -- runs until end of function | 898 | local res2 = table.pack(co()) -- runs until end of function |
| 871 | assert(res2.n == t.n) | 899 | assert(res2.n == t.n) |
| 872 | for i = 1, #t do | 900 | for i = 1, #t do |
| @@ -879,10 +907,10 @@ do | |||
| 879 | end | 907 | end |
| 880 | 908 | ||
| 881 | local function foo () | 909 | local function foo () |
| 882 | local x <close> = func2close(coroutine.yield) | 910 | local x <close> = func2close(coroutine.yield) -- "regular" yield |
| 883 | local extra <close> = func2close(function (self) | 911 | local extra <close> = func2close(function (self) |
| 884 | assert(self == extrares) | 912 | assert(self == extrares) |
| 885 | coroutine.yield(100) | 913 | coroutine.yield(100) -- first (extra) yield |
| 886 | end) | 914 | end) |
| 887 | extrares = extra | 915 | extrares = extra |
| 888 | return table.unpack{10, x, 30} | 916 | return table.unpack{10, x, 30} |
| @@ -891,21 +919,21 @@ do | |||
| 891 | assert(extrares == 100) | 919 | assert(extrares == 100) |
| 892 | 920 | ||
| 893 | local function foo () | 921 | local function foo () |
| 894 | local x <close> = func2close(coroutine.yield) | 922 | local x <close> = func2close(coroutine.yield) -- "regular" yield |
| 895 | return | 923 | return |
| 896 | end | 924 | end |
| 897 | check(foo, false) | 925 | check(foo, false) |
| 898 | 926 | ||
| 899 | local function foo () | 927 | local function foo () |
| 900 | local x <close> = func2close(coroutine.yield) | 928 | local x <close> = func2close(coroutine.yield) -- "regular" yield |
| 901 | local y, z = 20, 30 | 929 | local y, z = 20, 30 |
| 902 | return x | 930 | return x |
| 903 | end | 931 | end |
| 904 | check(foo, false, "x") | 932 | check(foo, false, "x") |
| 905 | 933 | ||
| 906 | local function foo () | 934 | local function foo () |
| 907 | local x <close> = func2close(coroutine.yield) | 935 | local x <close> = func2close(coroutine.yield) -- "regular" yield |
| 908 | local extra <close> = func2close(coroutine.yield) | 936 | local extra <close> = func2close(coroutine.yield) -- extra yield |
| 909 | return table.unpack({}, 1, 100) -- 100 nils | 937 | return table.unpack({}, 1, 100) -- 100 nils |
| 910 | end | 938 | end |
| 911 | check(foo, true, table.unpack({}, 1, 100)) | 939 | check(foo, true, table.unpack({}, 1, 100)) |
