Merge e7b68bb49c
into bf13ec7fd4
This commit is contained in:
commit
7aa56da56b
9 changed files with 118 additions and 177 deletions
|
@ -220,13 +220,6 @@ Returns an equivalent function that instead of throwing exceptions,
|
|||
returns <tt><b>nil</b></tt> followed by an error message.
|
||||
</p>
|
||||
|
||||
<p class=note>
|
||||
Note: Beware that if your function performs some illegal operation that
|
||||
raises an error, the protected function will catch the error and return it
|
||||
as a string. This is because the <a href=#try><tt>try</tt></a> function
|
||||
uses errors as the mechanism to throw exceptions.
|
||||
</p>
|
||||
|
||||
<!-- select +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
|
||||
|
||||
<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
|
|||
|
||||
<p class=description>
|
||||
Throws an exception in case of error. The exception can only be caught
|
||||
by the <a href=#protect><tt>protect</tt></a> function. It does not explode
|
||||
into an error message.
|
||||
by the <a href=#protect><tt>protect</tt></a> function.
|
||||
</p>
|
||||
|
||||
<p class=parameters>
|
||||
|
@ -436,7 +428,10 @@ nested with <tt>try</tt>.
|
|||
|
||||
<p class=return>
|
||||
The function returns <tt>ret</tt><sub>1</sub> to <tt>ret</tt><sub>N</sub> if
|
||||
<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>.
|
||||
<tt>ret</tt><sub>1</sub> is not <tt><b>nil</b></tt> or <tt><b>false</b></tt>.
|
||||
Otherwise, it calls <tt>error</tt> passing <tt>ret</tt><sub>2</sub> wrapped
|
||||
in a table with metatable used by <a href=#protect><tt>protect</tt></a> to
|
||||
distinguish exceptions from runtime errors.
|
||||
</p>
|
||||
|
||||
<pre class=example>
|
||||
|
|
|
@ -50,7 +50,7 @@ local function make_plat(plat)
|
|||
}
|
||||
local modules = {
|
||||
["socket.core"] = {
|
||||
sources = { "src/luasocket.c", "src/timeout.c", "src/buffer.c", "src/io.c", "src/auxiliar.c", "src/options.c", "src/inet.c", "src/except.c", "src/select.c", "src/tcp.c", "src/udp.c", "src/compat.c" },
|
||||
sources = { "src/luasocket.c", "src/timeout.c", "src/buffer.c", "src/io.c", "src/auxiliar.c", "src/options.c", "src/inet.c", "src/select.c", "src/tcp.c", "src/udp.c", "src/compat.c" },
|
||||
defines = defines[plat],
|
||||
incdir = "/src"
|
||||
},
|
||||
|
@ -65,6 +65,7 @@ local function make_plat(plat)
|
|||
["socket.ftp"] = "src/ftp.lua",
|
||||
["socket.headers"] = "src/headers.lua",
|
||||
["socket.smtp"] = "src/smtp.lua",
|
||||
["socket.except"] = "src/except.lua",
|
||||
ltn12 = "src/ltn12.lua",
|
||||
socket = "src/socket.lua",
|
||||
mime = "src/mime.lua"
|
||||
|
|
123
src/except.c
123
src/except.c
|
@ -1,123 +0,0 @@
|
|||
/*=========================================================================*\
|
||||
* Simple exception support
|
||||
* LuaSocket toolkit
|
||||
\*=========================================================================*/
|
||||
#include <stdio.h>
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include "except.h"
|
||||
|
||||
#if LUA_VERSION_NUM < 502
|
||||
#define lua_pcallk(L, na, nr, err, ctx, cont) \
|
||||
((void)ctx,(void)cont,lua_pcall(L, na, nr, err))
|
||||
#endif
|
||||
|
||||
#if LUA_VERSION_NUM < 503
|
||||
typedef int lua_KContext;
|
||||
#endif
|
||||
|
||||
/*=========================================================================*\
|
||||
* Internal function prototypes.
|
||||
\*=========================================================================*/
|
||||
static int global_protect(lua_State *L);
|
||||
static int global_newtry(lua_State *L);
|
||||
static int protected_(lua_State *L);
|
||||
static int finalize(lua_State *L);
|
||||
static int do_nothing(lua_State *L);
|
||||
|
||||
/* except functions */
|
||||
static luaL_Reg func[] = {
|
||||
{"newtry", global_newtry},
|
||||
{"protect", global_protect},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Try factory
|
||||
\*-------------------------------------------------------------------------*/
|
||||
static void wrap(lua_State *L) {
|
||||
lua_newtable(L);
|
||||
lua_pushnumber(L, 1);
|
||||
lua_pushvalue(L, -3);
|
||||
lua_settable(L, -3);
|
||||
lua_insert(L, -2);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
static int finalize(lua_State *L) {
|
||||
if (!lua_toboolean(L, 1)) {
|
||||
lua_pushvalue(L, lua_upvalueindex(1));
|
||||
lua_pcall(L, 0, 0, 0);
|
||||
lua_settop(L, 2);
|
||||
wrap(L);
|
||||
lua_error(L);
|
||||
return 0;
|
||||
} else return lua_gettop(L);
|
||||
}
|
||||
|
||||
static int do_nothing(lua_State *L) {
|
||||
(void) L;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int global_newtry(lua_State *L) {
|
||||
lua_settop(L, 1);
|
||||
if (lua_isnil(L, 1)) lua_pushcfunction(L, do_nothing);
|
||||
lua_pushcclosure(L, finalize, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Protect factory
|
||||
\*-------------------------------------------------------------------------*/
|
||||
static int unwrap(lua_State *L) {
|
||||
if (lua_istable(L, -1)) {
|
||||
lua_pushnumber(L, 1);
|
||||
lua_gettable(L, -2);
|
||||
lua_pushnil(L);
|
||||
lua_insert(L, -2);
|
||||
return 1;
|
||||
} else return 0;
|
||||
}
|
||||
|
||||
static int protected_finish(lua_State *L, int status, lua_KContext ctx) {
|
||||
(void)ctx;
|
||||
if (status != 0 && status != LUA_YIELD) {
|
||||
if (unwrap(L)) return 2;
|
||||
else return lua_error(L);
|
||||
} else return lua_gettop(L);
|
||||
}
|
||||
|
||||
#if LUA_VERSION_NUM == 502
|
||||
static int protected_cont(lua_State *L) {
|
||||
int ctx = 0;
|
||||
int status = lua_getctx(L, &ctx);
|
||||
return protected_finish(L, status, ctx);
|
||||
}
|
||||
#else
|
||||
#define protected_cont protected_finish
|
||||
#endif
|
||||
|
||||
static int protected_(lua_State *L) {
|
||||
int status;
|
||||
lua_pushvalue(L, lua_upvalueindex(1));
|
||||
lua_insert(L, 1);
|
||||
status = lua_pcallk(L, lua_gettop(L) - 1, LUA_MULTRET, 0, 0, protected_cont);
|
||||
return protected_finish(L, status, 0);
|
||||
}
|
||||
|
||||
static int global_protect(lua_State *L) {
|
||||
lua_pushcclosure(L, protected_, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*\
|
||||
* Init module
|
||||
\*-------------------------------------------------------------------------*/
|
||||
int except_open(lua_State *L) {
|
||||
luaL_setfuncs(L, func, 0);
|
||||
return 0;
|
||||
}
|
33
src/except.h
33
src/except.h
|
@ -1,33 +0,0 @@
|
|||
#ifndef EXCEPT_H
|
||||
#define EXCEPT_H
|
||||
/*=========================================================================*\
|
||||
* Exception control
|
||||
* LuaSocket toolkit (but completely independent from other modules)
|
||||
*
|
||||
* This provides support for simple exceptions in Lua. During the
|
||||
* development of the HTTP/FTP/SMTP support, it became aparent that
|
||||
* error checking was taking a substantial amount of the coding. These
|
||||
* function greatly simplify the task of checking errors.
|
||||
*
|
||||
* The main idea is that functions should return nil as its first return
|
||||
* value when it finds an error, and return an error message (or value)
|
||||
* following nil. In case of success, as long as the first value is not nil,
|
||||
* the other values don't matter.
|
||||
*
|
||||
* The idea is to nest function calls with the "try" function. This function
|
||||
* checks the first value, and calls "error" on the second if the first is
|
||||
* nil. Otherwise, it returns all values it received.
|
||||
*
|
||||
* The protect function returns a new function that behaves exactly like the
|
||||
* function it receives, but the new function doesn't throw exceptions: it
|
||||
* returns nil followed by the error message instead.
|
||||
*
|
||||
* With these two function, it's easy to write functions that throw
|
||||
* exceptions on error, but that don't interrupt the user script.
|
||||
\*=========================================================================*/
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
int except_open(lua_State *L);
|
||||
|
||||
#endif
|
76
src/except.lua
Normal file
76
src/except.lua
Normal file
|
@ -0,0 +1,76 @@
|
|||
-----------------------------------------------------------------------------
|
||||
-- Exception control
|
||||
-- LuaSocket toolkit (but completely independent from other modules)
|
||||
-- Author: Diego Nehab
|
||||
|
||||
-- This provides support for simple exceptions in Lua. During the
|
||||
-- development of the HTTP/FTP/SMTP support, it became aparent that
|
||||
-- error checking was taking a substantial amount of the coding. These
|
||||
-- function greatly simplify the task of checking errors.
|
||||
|
||||
-- The main idea is that functions should return nil as its first return
|
||||
-- value when it finds an error, and return an error message (or value)
|
||||
-- following nil. In case of success, as long as the first value is not nil,
|
||||
-- the other values don't matter.
|
||||
|
||||
-- The idea is to nest function calls with the "try" function. This function
|
||||
-- checks the first value, and, if it's falsy, wraps the second value
|
||||
-- in a table with metatable and calls "error" on it. Otherwise,
|
||||
-- it returns all values it received.
|
||||
|
||||
-- The "newtry" function is a factory for "try" functions that call a finalizer
|
||||
-- in protected mode before calling "error".
|
||||
|
||||
-- The "protect" function returns a new function that behaves exactly like the
|
||||
-- function it receives, but the new function catches exceptions
|
||||
-- thrown by "try" functions and returns nil followed by the error message
|
||||
-- instead.
|
||||
|
||||
-- With these three function, it's easy to write functions that throw
|
||||
-- exceptions on error, but that don't interrupt the user script.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
local base = _G
|
||||
local _M = {}
|
||||
|
||||
local exception_metat = {}
|
||||
|
||||
function exception_metat:__tostring()
|
||||
return base.tostring(self[1])
|
||||
end
|
||||
|
||||
local function do_nothing() end
|
||||
|
||||
function _M.newtry(finalizer)
|
||||
if finalizer == nil then finalizer = do_nothing end
|
||||
return function(...)
|
||||
local ok, err = ...
|
||||
if ok then
|
||||
return ...
|
||||
else
|
||||
base.pcall(finalizer)
|
||||
base.error(base.setmetatable({err}, exception_metat))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function handle_pcall_returns(ok, ...)
|
||||
if ok then
|
||||
return ...
|
||||
else
|
||||
local err = ...
|
||||
if base.getmetatable(err) == exception_metat then
|
||||
return nil, err[1]
|
||||
else
|
||||
base.error(err, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function _M.protect(func)
|
||||
return function(...)
|
||||
return handle_pcall_returns(base.pcall(func, ...))
|
||||
end
|
||||
end
|
||||
|
||||
return _M
|
|
@ -24,7 +24,6 @@
|
|||
\*=========================================================================*/
|
||||
#include "luasocket.h"
|
||||
#include "auxiliar.h"
|
||||
#include "except.h"
|
||||
#include "timeout.h"
|
||||
#include "buffer.h"
|
||||
#include "inet.h"
|
||||
|
@ -44,7 +43,6 @@ static int base_open(lua_State *L);
|
|||
\*-------------------------------------------------------------------------*/
|
||||
static const luaL_Reg mod[] = {
|
||||
{"auxiliar", auxiliar_open},
|
||||
{"except", except_open},
|
||||
{"timeout", timeout_open},
|
||||
{"buffer", buffer_open},
|
||||
{"inet", inet_open},
|
||||
|
|
|
@ -283,7 +283,6 @@ SOCKET_OBJS= \
|
|||
options.$(O) \
|
||||
inet.$(O) \
|
||||
$(SOCKET) \
|
||||
except.$(O) \
|
||||
select.$(O) \
|
||||
tcp.$(O) \
|
||||
udp.$(O)
|
||||
|
@ -328,7 +327,8 @@ TO_SOCKET_LDIR= \
|
|||
tp.lua \
|
||||
ftp.lua \
|
||||
headers.lua \
|
||||
smtp.lua
|
||||
smtp.lua \
|
||||
except.lua
|
||||
|
||||
TO_TOP_LDIR= \
|
||||
ltn12.lua \
|
||||
|
@ -410,10 +410,9 @@ clean:
|
|||
compat.$(O): compat.c compat.h
|
||||
auxiliar.$(O): auxiliar.c auxiliar.h
|
||||
buffer.$(O): buffer.c buffer.h io.h timeout.h
|
||||
except.$(O): except.c except.h
|
||||
inet.$(O): inet.c inet.h socket.h io.h timeout.h usocket.h
|
||||
io.$(O): io.c io.h timeout.h
|
||||
luasocket.$(O): luasocket.c luasocket.h auxiliar.h except.h \
|
||||
luasocket.$(O): luasocket.c luasocket.h auxiliar.h \
|
||||
timeout.h buffer.h io.h inet.h socket.h usocket.h tcp.h \
|
||||
udp.h select.h
|
||||
mime.$(O): mime.c mime.h
|
||||
|
|
|
@ -10,6 +10,7 @@ local base = _G
|
|||
local string = require("string")
|
||||
local math = require("math")
|
||||
local socket = require("socket.core")
|
||||
local except = require("socket.except")
|
||||
|
||||
local _M = socket
|
||||
|
||||
|
@ -53,7 +54,9 @@ function _M.bind(host, port, backlog)
|
|||
return nil, err
|
||||
end
|
||||
|
||||
_M.newtry = except.newtry
|
||||
_M.try = _M.newtry()
|
||||
_M.protect = except.protect
|
||||
|
||||
function _M.choose(table)
|
||||
return function(name, opt1, opt2)
|
||||
|
|
|
@ -1,6 +1,31 @@
|
|||
local socket = require("socket")
|
||||
try = socket.newtry(function()
|
||||
print("finalized!!!")
|
||||
|
||||
local finalizer_called
|
||||
|
||||
local func = socket.protect(function(err, ...)
|
||||
local try = socket.newtry(function()
|
||||
finalizer_called = true
|
||||
error("ignored")
|
||||
end)
|
||||
|
||||
if err then
|
||||
return error(err, 0)
|
||||
else
|
||||
return try(...)
|
||||
end
|
||||
end)
|
||||
try = socket.protect(try)
|
||||
print(try(nil, "it works"))
|
||||
|
||||
local ret1, ret2, ret3 = func(false, 1, 2, 3)
|
||||
assert(not finalizer_called, "unexpected finalizer call")
|
||||
assert(ret1 == 1 and ret2 == 2 and ret3 == 3, "incorrect return values")
|
||||
|
||||
ret1, ret2, ret3 = func(false, false, "error message")
|
||||
assert(finalizer_called, "finalizer not called")
|
||||
assert(ret1 == nil and ret2 == "error message" and ret3 == nil, "incorrect return values")
|
||||
|
||||
local err = {key = "value"}
|
||||
ret1, ret2 = pcall(func, err)
|
||||
assert(not ret1, "error not rethrown")
|
||||
assert(ret2 == err, "incorrect error rethrown")
|
||||
|
||||
print("OK")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue