diff options
author | Diego Nehab <diego.nehab@gmail.com> | 2016-02-23 14:30:35 -0300 |
---|---|---|
committer | Diego Nehab <diego.nehab@gmail.com> | 2016-02-23 14:30:35 -0300 |
commit | 9ffd96724da09352abe99c8525bbccb6bc34cb48 (patch) | |
tree | 8d55377133839458dba7ff55b494146ab667e31d | |
parent | bf13ec7fd4fb05666964cf629e9b10591356ff67 (diff) | |
parent | 7cab8a5006da807d83f427217e2158079bb78145 (diff) | |
download | luasocket-9ffd96724da09352abe99c8525bbccb6bc34cb48.tar.gz luasocket-9ffd96724da09352abe99c8525bbccb6bc34cb48.tar.bz2 luasocket-9ffd96724da09352abe99c8525bbccb6bc34cb48.zip |
Merge pull request #162 from siffiejoe/exception-meta
Support table errors in LuaSockets LTN13 C implementation.
-rw-r--r-- | doc/socket.html | 15 | ||||
-rw-r--r-- | src/except.c | 46 | ||||
-rw-r--r-- | src/except.h | 23 | ||||
-rw-r--r-- | test/excepttest.lua | 33 |
4 files changed, 75 insertions, 42 deletions
diff --git a/doc/socket.html b/doc/socket.html index 8a81414..a43a208 100644 --- a/doc/socket.html +++ b/doc/socket.html | |||
@@ -220,13 +220,6 @@ Returns an equivalent function that instead of throwing exceptions, | |||
220 | returns <tt><b>nil</b></tt> followed by an error message. | 220 | returns <tt><b>nil</b></tt> followed by an error message. |
221 | </p> | 221 | </p> |
222 | 222 | ||
223 | <p class=note> | ||
224 | Note: Beware that if your function performs some illegal operation that | ||
225 | raises an error, the protected function will catch the error and return it | ||
226 | as a string. This is because the <a href=#try><tt>try</tt></a> function | ||
227 | uses errors as the mechanism to throw exceptions. | ||
228 | </p> | ||
229 | |||
230 | <!-- select +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | 223 | <!-- select +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> |
231 | 224 | ||
232 | <p class=name id=select> | 225 | <p class=name id=select> |
@@ -424,8 +417,7 @@ socket.<b>try(</b>ret<sub>1</sub> [, ret<sub>2</sub> ... ret<sub>N</sub>]<b>)</b | |||
424 | 417 | ||
425 | <p class=description> | 418 | <p class=description> |
426 | Throws an exception in case of error. The exception can only be caught | 419 | Throws an exception in case of error. The exception can only be caught |
427 | by the <a href=#protect><tt>protect</tt></a> function. It does not explode | 420 | by the <a href=#protect><tt>protect</tt></a> function. |
428 | into an error message. | ||
429 | </p> | 421 | </p> |
430 | 422 | ||
431 | <p class=parameters> | 423 | <p class=parameters> |
@@ -436,7 +428,10 @@ nested with <tt>try</tt>. | |||
436 | 428 | ||
437 | <p class=return> | 429 | <p class=return> |
438 | The function returns <tt>ret</tt><sub>1</sub> to <tt>ret</tt><sub>N</sub> if | 430 | The function returns <tt>ret</tt><sub>1</sub> to <tt>ret</tt><sub>N</sub> if |
439 | <tt>ret</tt><sub>1</sub> is not <tt><b>nil</b></tt>. Otherwise, it calls <tt>error</tt> passing <tt>ret</tt><sub>2</sub>. | 431 | <tt>ret</tt><sub>1</sub> is not <tt><b>nil</b></tt> or <tt><b>false</b></tt>. |
432 | Otherwise, it calls <tt>error</tt> passing <tt>ret</tt><sub>2</sub> wrapped | ||
433 | in a table with metatable used by <a href=#protect><tt>protect</tt></a> to | ||
434 | distinguish exceptions from runtime errors. | ||
440 | </p> | 435 | </p> |
441 | 436 | ||
442 | <pre class=example> | 437 | <pre class=example> |
diff --git a/src/except.c b/src/except.c index 261ac98..def35a0 100644 --- a/src/except.c +++ b/src/except.c | |||
@@ -12,7 +12,7 @@ | |||
12 | 12 | ||
13 | #if LUA_VERSION_NUM < 502 | 13 | #if LUA_VERSION_NUM < 502 |
14 | #define lua_pcallk(L, na, nr, err, ctx, cont) \ | 14 | #define lua_pcallk(L, na, nr, err, ctx, cont) \ |
15 | ((void)ctx,(void)cont,lua_pcall(L, na, nr, err)) | 15 | (((void)ctx),((void)cont),lua_pcall(L, na, nr, err)) |
16 | #endif | 16 | #endif |
17 | 17 | ||
18 | #if LUA_VERSION_NUM < 503 | 18 | #if LUA_VERSION_NUM < 503 |
@@ -39,12 +39,11 @@ static luaL_Reg func[] = { | |||
39 | * Try factory | 39 | * Try factory |
40 | \*-------------------------------------------------------------------------*/ | 40 | \*-------------------------------------------------------------------------*/ |
41 | static void wrap(lua_State *L) { | 41 | static void wrap(lua_State *L) { |
42 | lua_newtable(L); | 42 | lua_createtable(L, 1, 0); |
43 | lua_pushnumber(L, 1); | 43 | lua_pushvalue(L, -2); |
44 | lua_pushvalue(L, -3); | 44 | lua_rawseti(L, -2, 1); |
45 | lua_settable(L, -3); | 45 | lua_pushvalue(L, lua_upvalueindex(2)); |
46 | lua_insert(L, -2); | 46 | lua_setmetatable(L, -2); |
47 | lua_pop(L, 1); | ||
48 | } | 47 | } |
49 | 48 | ||
50 | static int finalize(lua_State *L) { | 49 | static int finalize(lua_State *L) { |
@@ -58,15 +57,16 @@ static int finalize(lua_State *L) { | |||
58 | } else return lua_gettop(L); | 57 | } else return lua_gettop(L); |
59 | } | 58 | } |
60 | 59 | ||
61 | static int do_nothing(lua_State *L) { | 60 | static int do_nothing(lua_State *L) { |
62 | (void) L; | 61 | (void) L; |
63 | return 0; | 62 | return 0; |
64 | } | 63 | } |
65 | 64 | ||
66 | static int global_newtry(lua_State *L) { | 65 | static int global_newtry(lua_State *L) { |
67 | lua_settop(L, 1); | 66 | lua_settop(L, 1); |
68 | if (lua_isnil(L, 1)) lua_pushcfunction(L, do_nothing); | 67 | if (lua_isnil(L, 1)) lua_pushcfunction(L, do_nothing); |
69 | lua_pushcclosure(L, finalize, 1); | 68 | lua_pushvalue(L, lua_upvalueindex(1)); |
69 | lua_pushcclosure(L, finalize, 2); | ||
70 | return 1; | 70 | return 1; |
71 | } | 71 | } |
72 | 72 | ||
@@ -74,13 +74,16 @@ static int global_newtry(lua_State *L) { | |||
74 | * Protect factory | 74 | * Protect factory |
75 | \*-------------------------------------------------------------------------*/ | 75 | \*-------------------------------------------------------------------------*/ |
76 | static int unwrap(lua_State *L) { | 76 | static int unwrap(lua_State *L) { |
77 | if (lua_istable(L, -1)) { | 77 | if (lua_istable(L, -1) && lua_getmetatable(L, -1)) { |
78 | lua_pushnumber(L, 1); | 78 | int r = lua_rawequal(L, -1, lua_upvalueindex(2)); |
79 | lua_gettable(L, -2); | 79 | lua_pop(L, 1); |
80 | lua_pushnil(L); | 80 | if (r) { |
81 | lua_insert(L, -2); | 81 | lua_pushnil(L); |
82 | return 1; | 82 | lua_rawgeti(L, -2, 1); |
83 | } else return 0; | 83 | return 1; |
84 | } | ||
85 | } | ||
86 | return 0; | ||
84 | } | 87 | } |
85 | 88 | ||
86 | static int protected_finish(lua_State *L, int status, lua_KContext ctx) { | 89 | static int protected_finish(lua_State *L, int status, lua_KContext ctx) { |
@@ -110,7 +113,9 @@ static int protected_(lua_State *L) { | |||
110 | } | 113 | } |
111 | 114 | ||
112 | static int global_protect(lua_State *L) { | 115 | static int global_protect(lua_State *L) { |
113 | lua_pushcclosure(L, protected_, 1); | 116 | lua_settop(L, 1); |
117 | lua_pushvalue(L, lua_upvalueindex(1)); | ||
118 | lua_pushcclosure(L, protected_, 2); | ||
114 | return 1; | 119 | return 1; |
115 | } | 120 | } |
116 | 121 | ||
@@ -118,6 +123,9 @@ static int global_protect(lua_State *L) { | |||
118 | * Init module | 123 | * Init module |
119 | \*-------------------------------------------------------------------------*/ | 124 | \*-------------------------------------------------------------------------*/ |
120 | int except_open(lua_State *L) { | 125 | int except_open(lua_State *L) { |
121 | luaL_setfuncs(L, func, 0); | 126 | lua_newtable(L); /* metatable for wrapped exceptions */ |
127 | lua_pushboolean(L, 0); | ||
128 | lua_setfield(L, -2, "__metatable"); | ||
129 | luaL_setfuncs(L, func, 1); | ||
122 | return 0; | 130 | return 0; |
123 | } | 131 | } |
diff --git a/src/except.h b/src/except.h index 1e7a245..2497c05 100644 --- a/src/except.h +++ b/src/except.h | |||
@@ -9,21 +9,26 @@ | |||
9 | * error checking was taking a substantial amount of the coding. These | 9 | * error checking was taking a substantial amount of the coding. These |
10 | * function greatly simplify the task of checking errors. | 10 | * function greatly simplify the task of checking errors. |
11 | * | 11 | * |
12 | * The main idea is that functions should return nil as its first return | 12 | * The main idea is that functions should return nil as their first return |
13 | * value when it finds an error, and return an error message (or value) | 13 | * values when they find an error, and return an error message (or value) |
14 | * following nil. In case of success, as long as the first value is not nil, | 14 | * following nil. In case of success, as long as the first value is not nil, |
15 | * the other values don't matter. | 15 | * the other values don't matter. |
16 | * | 16 | * |
17 | * The idea is to nest function calls with the "try" function. This function | 17 | * The idea is to nest function calls with the "try" function. This function |
18 | * checks the first value, and calls "error" on the second if the first is | 18 | * checks the first value, and, if it's falsy, wraps the second value in a |
19 | * nil. Otherwise, it returns all values it received. | 19 | * table with metatable and calls "error" on it. Otherwise, it returns all |
20 | * values it received. Basically, it works like the Lua "assert" function, | ||
21 | * but it creates errors targeted specifically at "protect". | ||
20 | * | 22 | * |
21 | * The protect function returns a new function that behaves exactly like the | 23 | * The "newtry" function is a factory for "try" functions that call a |
22 | * function it receives, but the new function doesn't throw exceptions: it | 24 | * finalizer in protected mode before calling "error". |
23 | * returns nil followed by the error message instead. | ||
24 | * | 25 | * |
25 | * With these two function, it's easy to write functions that throw | 26 | * The "protect" function returns a new function that behaves exactly like |
26 | * exceptions on error, but that don't interrupt the user script. | 27 | * the function it receives, but the new function catches exceptions thrown |
28 | * by "try" functions and returns nil followed by the error message instead. | ||
29 | * | ||
30 | * With these three functions, it's easy to write functions that throw | ||
31 | * exceptions on error, but that don't interrupt the user script. | ||
27 | \*=========================================================================*/ | 32 | \*=========================================================================*/ |
28 | 33 | ||
29 | #include "lua.h" | 34 | #include "lua.h" |
diff --git a/test/excepttest.lua b/test/excepttest.lua index ce9f197..6904545 100644 --- a/test/excepttest.lua +++ b/test/excepttest.lua | |||
@@ -1,6 +1,31 @@ | |||
1 | local socket = require("socket") | 1 | local socket = require("socket") |
2 | try = socket.newtry(function() | 2 | |
3 | print("finalized!!!") | 3 | local finalizer_called |
4 | |||
5 | local func = socket.protect(function(err, ...) | ||
6 | local try = socket.newtry(function() | ||
7 | finalizer_called = true | ||
8 | error("ignored") | ||
9 | end) | ||
10 | |||
11 | if err then | ||
12 | return error(err, 0) | ||
13 | else | ||
14 | return try(...) | ||
15 | end | ||
4 | end) | 16 | end) |
5 | try = socket.protect(try) | 17 | |
6 | print(try(nil, "it works")) | 18 | local ret1, ret2, ret3 = func(false, 1, 2, 3) |
19 | assert(not finalizer_called, "unexpected finalizer call") | ||
20 | assert(ret1 == 1 and ret2 == 2 and ret3 == 3, "incorrect return values") | ||
21 | |||
22 | ret1, ret2, ret3 = func(false, false, "error message") | ||
23 | assert(finalizer_called, "finalizer not called") | ||
24 | assert(ret1 == nil and ret2 == "error message" and ret3 == nil, "incorrect return values") | ||
25 | |||
26 | local err = {key = "value"} | ||
27 | ret1, ret2 = pcall(func, err) | ||
28 | assert(not ret1, "error not rethrown") | ||
29 | assert(ret2 == err, "incorrect error rethrown") | ||
30 | |||
31 | print("OK") | ||