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") | ||
