# HG changeset patch # User David Demelier # Date 1523653556 -7200 # Node ID a059100b73dc4b30f7cbfe242a8d98376c02cf98 # Parent e81c6badede3d271f7cd988ccbbd57f2e84eaa1c Irccd: wrap all possible insecure functions in Javascript API, closes #794 @2h To avoid interfering with the Duktape stack, do not throw any exception in all Javascript API, wrap all function and convert them into Duktape errors. diff -r e81c6badede3 -r a059100b73dc libirccd-js/irccd/js/directory_jsapi.cpp --- a/libirccd-js/irccd/js/directory_jsapi.cpp Fri Apr 13 22:43:10 2018 +0200 +++ b/libirccd-js/irccd/js/directory_jsapi.cpp Fri Apr 13 23:05:56 2018 +0200 @@ -38,6 +38,22 @@ namespace { +template +duk_ret_t wrap(duk_context* ctx, Handler handler) +{ + try { + return handler(); + } catch (const boost::system::system_error& ex) { + dukx_throw(ctx, ex); + } catch (const std::system_error& ex) { + dukx_throw(ctx, ex); + } catch (const std::exception& ex) { + dukx_throw(ctx, ex); + } + + return 0; +} + std::string path(duk_context *ctx) { duk_push_this(ctx); @@ -59,73 +75,78 @@ /* * Generic find function for: * - * - Directory.find - * - Directory.prototype.find + * - Irccd.Directory.find + * - Irccd.Directory.prototype.find * - * The patternIndex is the argument where to test if the argument is a regex or + * The pattern_index is the argument where to test if the argument is a regex or * a string. */ duk_ret_t find(duk_context* ctx, std::string base, bool recursive, int pattern_index) { - try { - std::string path; + /* + * Helper for checking if it's a valid RegExp object. + */ + const auto is_regex = [&] { + duk_get_global_string(ctx, "RegExp"); + const auto result = duk_instanceof(ctx, pattern_index, -1); + duk_pop(ctx); - if (duk_is_string(ctx, pattern_index)) - path = fs_util::find(base, dukx_get(ctx, pattern_index), recursive); - else { - // Check if it's a valid RegExp object. - duk_get_global_string(ctx, "RegExp"); - const auto is_regex = duk_instanceof(ctx, pattern_index, -1); - duk_pop(ctx); + return result; + }; - if (is_regex) { - duk_get_prop_string(ctx, pattern_index, "source"); - const auto pattern = duk_to_string(ctx, -1); - duk_pop(ctx); + /** + * Helper for getting regex source. + */ + const auto pattern = [&] { + duk_get_prop_string(ctx, pattern_index, "source"); + const auto pattern = duk_to_string(ctx, -1); + duk_pop(ctx); - path = fs_util::find(base, std::regex(pattern), recursive); - } else - duk_error(ctx, DUK_ERR_TYPE_ERROR, "pattern must be a string or a regex expression"); - } + return pattern; + }; - if (path.empty()) - return 0; + std::string path; - dukx_push(ctx, path); - } catch (const std::exception& ex) { - duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what()); - } + if (duk_is_string(ctx, pattern_index)) + path = fs_util::find(base, dukx_get(ctx, pattern_index), recursive); + else if (is_regex()) + path = fs_util::find(base, pattern(), recursive); + else + throw dukx_type_error("pattern must be a string or a regex expression"); - return 1; + if (path.empty()) + return 0; + + return dukx_push(ctx, path); } /* * Generic remove function for: * - * - Directory.remove - * - Directory.prototype.remove + * - Irccd.Directory.remove + * - Irccd.Directory.prototype.remove */ -duk_ret_t remove(duk_context* ctx, const std::string& path, bool recursive) +duk_ret_t remove(const std::string& path, bool recursive) { - boost::system::error_code ec; - - if (!boost::filesystem::is_directory(path, ec) || ec) - dukx_throw(ctx, system_error(EINVAL, "not a directory")); + if (!boost::filesystem::is_directory(path)) + throw std::system_error(make_error_code(std::errc::invalid_argument)); if (!recursive) - boost::filesystem::remove(path, ec); + boost::filesystem::remove(path); else - boost::filesystem::remove_all(path, ec); + boost::filesystem::remove_all(path); return 0; } +// {{{ Irccd.Directory.prototype.find + /* - * Method: Directory.find(pattern, recursive) + * Method: Irccd.Directory.prototype.find(pattern, recursive) * -------------------------------------------------------- * - * Synonym of Directory.find(path, pattern, recursive) but the path is taken - * from the directory object. + * Synonym of Irccd.Directory.find(path, pattern, recursive) but the path is + * taken from the directory object. * * Arguments: * - pattern, the regular expression or file name, @@ -133,15 +154,21 @@ * Returns: * The path to the file or undefined if not found. * Throws: - * - Any exception on error. + * - Irccd.SystemError on errors */ -duk_ret_t method_find(duk_context* ctx) +duk_ret_t Directory_prototype_find(duk_context* ctx) { - return find(ctx, path(ctx), duk_get_boolean(ctx, 1), 0); + return wrap(ctx, [&] { + return find(ctx, path(ctx), dukx_get(ctx, 1), 0); + }); } +// }}} + +// {{{ Irccd.Directory.prototype.remove + /* - * Method: Directory.remove(recursive) + * Method: Irccd.Directory.prototype.remove(recursive) * -------------------------------------------------------- * * Synonym of Directory.remove(recursive) but the path is taken from the @@ -150,23 +177,18 @@ * Arguments: * - recursive, recursively or not (default: false). * Throws: - * - Any exception on error. + * - Irccd.SystemError on errors */ -duk_ret_t method_remove(duk_context* ctx) +duk_ret_t Directory_prototype_remove(duk_context* ctx) { - return remove(ctx, path(ctx), duk_get_boolean(ctx, 0)); + return wrap(ctx, [&] { + return remove(path(ctx), dukx_get(ctx, 0)); + }); } -const duk_function_list_entry methods[] = { - { "find", method_find, DUK_VARARGS }, - { "remove", method_remove, 1 }, - { nullptr, nullptr, 0 } -}; +// }}} -/* - * Directory "static" functions - * ------------------------------------------------------------------ - */ +// {{{ Irccd.Directory [constructor] /* * Function: Irccd.Directory(path) [constructor] @@ -177,18 +199,18 @@ * Arguments: * - path, the path to the directory, * Throws: - * - Any exception on error + * - Irccd.SystemError on errors */ -duk_ret_t constructor(duk_context* ctx) +duk_ret_t Directory_constructor(duk_context* ctx) { - if (!duk_is_constructor_call(ctx)) - return 0; + return wrap(ctx, [&] { + if (!duk_is_constructor_call(ctx)) + return 0; - try { - auto path = duk_require_string(ctx, 0); + const auto path = dukx_require(ctx, 0); if (!boost::filesystem::is_directory(path)) - dukx_throw(ctx, system_error(EINVAL, "not a directory")); + throw std::system_error(make_error_code(std::errc::invalid_argument)); duk_push_this(ctx); @@ -212,15 +234,17 @@ dukx_push(ctx, "path"); dukx_push(ctx, path); duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); - } catch (const std::exception& ex) { - dukx_throw(ctx, system_error(errno, ex.what())); - } - return 0; + return 0; + }); } +// }}} + +// {{{ Irccd.Directory.find + /* - * Function: irccd.Directory.find(path, pattern, recursive) + * Function: Irccd.Directory.find(path, pattern, recursive) * -------------------------------------------------------- * * Find an entry by a pattern or a regular expression. @@ -231,14 +255,22 @@ * - recursive, set to true to search recursively (default: false). * Returns: * The path to the file or undefined on errors or not found. + * Throws: + * - Irccd.SystemError on errors */ -duk_ret_t func_find(duk_context* ctx) +duk_ret_t Directory_find(duk_context* ctx) { - return find(ctx, duk_require_string(ctx, 0), duk_get_boolean(ctx, 2), 1); + return wrap(ctx, [&] { + return find(ctx, dukx_require(ctx, 0), dukx_get(ctx, 2), 1); + }); } +// }}} + +// {{{ Irccd.Directory.remove + /* - * Function: irccd.Directory.remove(path, recursive) + * Function: Irccd.Directory.remove(path, recursive) * -------------------------------------------------------- * * Remove the directory optionally recursively. @@ -247,15 +279,21 @@ * - path, the path to the directory, * - recursive, recursively or not (default: false). * Throws: - * - Any exception on error. + * - Irccd.SystemError on errors */ -duk_ret_t func_remove(duk_context *ctx) +duk_ret_t Directory_remove(duk_context *ctx) { - return remove(ctx, duk_require_string(ctx, 0), duk_get_boolean(ctx, 1)); + return wrap(ctx, [&] { + return remove(dukx_require(ctx, 0), dukx_get(ctx, 1)); + }); } +// }}} + +// {{{ Irccd.Directory.mkdir + /* - * Function: irccd.Directory.mkdir(path, mode = 0700) + * Function: Irccd.Directory.mkdir(path, mode = 0700) * -------------------------------------------------------- * * Create a directory specified by path. It will create needed subdirectories @@ -264,36 +302,42 @@ * Arguments: * - path, the path to the directory, * Throws: - * - Any exception on error. + * - Irccd.SystemError on errors */ -duk_ret_t func_mkdir(duk_context *ctx) +duk_ret_t Directory_mkdir(duk_context *ctx) { - try { - boost::filesystem::create_directories(duk_require_string(ctx, 0)); - } catch (const std::exception &ex) { - dukx_throw(ctx, system_error(errno, ex.what())); - } + return wrap(ctx, [&] { + boost::filesystem::create_directories(dukx_require(ctx, 0)); - return 0; + return 0; + }); } +// }}} + +const duk_function_list_entry methods[] = { + { "find", Directory_prototype_find, DUK_VARARGS }, + { "remove", Directory_prototype_remove, 1 }, + { nullptr, nullptr, 0 } +}; + const duk_function_list_entry functions[] = { - { "find", func_find, DUK_VARARGS }, - { "mkdir", func_mkdir, DUK_VARARGS }, - { "remove", func_remove, DUK_VARARGS }, - { nullptr, nullptr, 0 } + { "find", Directory_find, DUK_VARARGS }, + { "mkdir", Directory_mkdir, DUK_VARARGS }, + { "remove", Directory_remove, DUK_VARARGS }, + { nullptr, nullptr, 0 } }; const duk_number_list_entry constants[] = { - { "TypeFile", static_cast(fs::regular_file) }, - { "TypeDir", static_cast(fs::directory_file) }, - { "TypeLink", static_cast(fs::symlink_file) }, - { "TypeBlock", static_cast(fs::block_file) }, - { "TypeCharacter", static_cast(fs::character_file) }, - { "TypeFifo", static_cast(fs::fifo_file) }, - { "TypeSocket", static_cast(fs::socket_file) }, - { "TypeUnknown", static_cast(fs::type_unknown) }, - { nullptr, 0 } + { "TypeFile", static_cast(fs::regular_file) }, + { "TypeDir", static_cast(fs::directory_file) }, + { "TypeLink", static_cast(fs::symlink_file) }, + { "TypeBlock", static_cast(fs::block_file) }, + { "TypeCharacter", static_cast(fs::character_file) }, + { "TypeFifo", static_cast(fs::fifo_file) }, + { "TypeSocket", static_cast(fs::socket_file) }, + { "TypeUnknown", static_cast(fs::type_unknown) }, + { nullptr, 0 } }; } // !namespace @@ -308,7 +352,7 @@ dukx_stack_assert sa(plugin->context()); duk_get_global_string(plugin->context(), "Irccd"); - duk_push_c_function(plugin->context(), constructor, 2); + duk_push_c_function(plugin->context(), Directory_constructor, 2); duk_put_number_list(plugin->context(), -1, constants); duk_put_function_list(plugin->context(), -1, functions); diff -r e81c6badede3 -r a059100b73dc libirccd-js/irccd/js/duktape.hpp --- a/libirccd-js/irccd/js/duktape.hpp Fri Apr 13 22:43:10 2018 +0200 +++ b/libirccd-js/irccd/js/duktape.hpp Fri Apr 13 23:05:56 2018 +0200 @@ -255,6 +255,162 @@ }; /** + * \brief Base ECMAScript error class. + * \warning Override the function create for your own exceptions + */ +class dukx_error { +private: + int type_{DUK_ERR_ERROR}; + std::string message_; + +protected: + /** + * Constructor with a type of error specified, specially designed for + * derived errors. + * + * \param type of error (e.g. DUK_ERR_ERROR) + * \param message the message + */ + inline dukx_error(int type, std::string message) noexcept + : type_(type) + , message_(std::move(message)) + { + } + +public: + /** + * Constructor with a message. + * + * \param message the message + */ + inline dukx_error(std::string message) noexcept + : message_(std::move(message)) + { + } + + /** + * Virtual destructor defaulted. + */ + virtual ~dukx_error() = default; + + /** + * Get internal Duktape error (e.g. DUK_ERR_TYPE_ERROR) + * + * \return the type + */ + inline int get_type() const noexcept + { + return type_; + } + + /** + * Get the error message. + * + * \return the message + */ + inline const std::string& get_message() const noexcept + { + return message_; + } +}; + +/** + * \brief Error in eval() function. + */ +class dukx_eval_error : public dukx_error { +public: + /** + * Construct an EvalError. + * + * \param message the message + */ + inline dukx_eval_error(std::string message) noexcept + : dukx_error(DUK_ERR_EVAL_ERROR, std::move(message)) + { + } +}; + +/** + * \brief Value is out of range. + */ +class dukx_range_error : public dukx_error { +public: + /** + * Construct an RangeError. + * + * \param message the message + */ + inline dukx_range_error(std::string message) noexcept + : dukx_error(DUK_ERR_RANGE_ERROR, std::move(message)) + { + } +}; + +/** + * \brief Trying to use a variable that does not exist. + */ +class dukx_reference_error : public dukx_error { +public: + /** + * Construct an ReferenceError. + * + * \param message the message + */ + inline dukx_reference_error(std::string message) noexcept + : dukx_error(DUK_ERR_REFERENCE_ERROR, std::move(message)) + { + } +}; + +/** + * \brief Syntax error in the script. + */ +class dukx_syntax_error : public dukx_error { +public: + /** + * Construct an SyntaxError. + * + * \param message the message + */ + inline dukx_syntax_error(std::string message) noexcept + : dukx_error(DUK_ERR_SYNTAX_ERROR, std::move(message)) + { + } +}; + +/** + * \brief Invalid type given. + */ +class dukx_type_error : public dukx_error { +public: + /** + * Construct an TypeError. + * + * \param message the message + */ + inline dukx_type_error(std::string message) noexcept + : dukx_error(DUK_ERR_TYPE_ERROR, std::move(message)) + { + } +}; + +/** + * \brief URI manipulation failure. + */ +class dukx_uri_error : public dukx_error { +public: + /** + * Construct an URIError. + * + * \param message the message + */ + inline dukx_uri_error(std::string message) noexcept + : dukx_error(DUK_ERR_URI_ERROR, std::move(message)) + { + } +}; + +/** * \brief Operations on different types. * * This class provides some functions for the given type, depending on the @@ -280,6 +436,20 @@ * - `duk_double_t`, * - `const char*`, * - `std::string` + * + * It is also specialized for all exceptions types: + * + * - `dukx_error`, + * - `dukx_eval_error`, + * - `dukx_range_error`, + * - `dukx_reference_error`, + * - `dukx_syntax_error`, + * - `dukx_type_error`, + * - `dukx_uri_error`. + * + * And more general std::exception: + * + * - `std::exception`. */ template class dukx_type_traits : public std::false_type { @@ -580,6 +750,83 @@ }; /** + * \brief Specialization for dukx_error. + */ +template <> +class dukx_type_traits : public std::true_type { +public: + /** + * Create the exception on the stack. + * + * \param ctx the context + * \param ex the error + */ + static void raise(duk_context* ctx, const dukx_error& ex) + { + duk_error(ctx, ex.get_type(), "%s", ex.get_message().c_str()); + } +}; + +/** + * \brief Specialization for dukx_eval_error. + */ +template <> +class dukx_type_traits : public dukx_type_traits { +}; + +/** + * \brief Specialization for dukx_range_error. + */ +template <> +class dukx_type_traits : public dukx_type_traits { +}; + +/** + * \brief Specialization for dukx_reference_error. + */ +template <> +class dukx_type_traits : public dukx_type_traits { +}; + +/** + * \brief Specialization for dukx_syntax_error. + */ +template <> +class dukx_type_traits : public dukx_type_traits { +}; + +/** + * \brief Specialization for dukx_type_error. + */ +template <> +class dukx_type_traits : public dukx_type_traits { +}; + +/** + * \brief Specialization for dukx_uri_error. + */ +template <> +class dukx_type_traits : public dukx_type_traits { +}; + +/** + * \brief Specialization for std::exception. + */ +template <> +class dukx_type_traits : public std::true_type { +public: + /** + * Raise std::exception as general DUK_ERR_ERROR. + * + * \param ctx the context + * \param ex the exception + */ + static void raise(duk_context* ctx, const std::exception& ex) { + duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what()); + } +}; + +/** * \brief Partial specialization for collections. * * Derive from this class to implement type traits for collections. @@ -784,164 +1031,23 @@ } /** - * \brief Base ECMAScript error class. - * \warning Override the function create for your own exceptions - */ -class dukx_error { -private: - int type_{DUK_ERR_ERROR}; - std::string message_; - -protected: - /** - * Constructor with a type of error specified, specially designed for - * derived errors. - * - * \param type of error (e.g. DUK_ERR_ERROR) - * \param message the message - */ - inline dukx_error(int type, std::string message) noexcept - : type_(type) - , message_(std::move(message)) - { - } - -public: - /** - * Constructor with a message. - * - * \param message the message - */ - inline dukx_error(std::string message) noexcept - : message_(std::move(message)) - { - } - - /** - * Virtual destructor defaulted. - */ - virtual ~dukx_error() = default; - - /** - * Create the exception on the stack. - * - * \note the default implementation search for the global variables - * \param ctx the context - */ - void create(duk_context* ctx) const - { - duk_push_error_object(ctx, type_, "%s", message_.c_str()); - } -}; - -/** - * \brief Error in eval() function. - */ -class dukx_eval_error : public dukx_error { -public: - /** - * Construct an EvalError. - * - * \param message the message - */ - inline dukx_eval_error(std::string message) noexcept - : dukx_error(DUK_ERR_EVAL_ERROR, std::move(message)) - { - } -}; - -/** - * \brief Value is out of range. - */ -class dukx_range_error : public dukx_error { -public: - /** - * Construct an RangeError. - * - * \param message the message - */ - inline dukx_range_error(std::string message) noexcept - : dukx_error(DUK_ERR_RANGE_ERROR, std::move(message)) - { - } -}; - -/** - * \brief Trying to use a variable that does not exist. - */ -class dukx_reference_error : public dukx_error { -public: - /** - * Construct an ReferenceError. - * - * \param message the message - */ - inline dukx_reference_error(std::string message) noexcept - : dukx_error(DUK_ERR_REFERENCE_ERROR, std::move(message)) - { - } -}; - -/** - * \brief Syntax error in the script. - */ -class dukx_syntax_error : public dukx_error { -public: - /** - * Construct an SyntaxError. - * - * \param message the message - */ - inline dukx_syntax_error(std::string message) noexcept - : dukx_error(DUK_ERR_SYNTAX_ERROR, std::move(message)) - { - } -}; - -/** - * \brief Invalid type given. - */ -class dukx_type_error : public dukx_error { -public: - /** - * Construct an TypeError. - * - * \param message the message - */ - inline dukx_type_error(std::string message) noexcept - : dukx_error(DUK_ERR_TYPE_ERROR, std::move(message)) - { - } -}; - -/** - * \brief URI manipulation failure. - */ -class dukx_uri_error : public dukx_error { -public: - /** - * Construct an URIError. - * - * \param message the message - */ - inline dukx_uri_error(std::string message) noexcept - : dukx_error(DUK_ERR_URI_ERROR, std::move(message)) - { - } -}; - -/** * Create an exception into the stack and throws it. * + * The dukx_type_traits must have the following function: + * + * ``` + * static void raise(const Error&); + * ``` + * * \param ctx the Duktape context * \param error the error object */ template void dukx_throw(duk_context* ctx, const Error& error) { - error.create(ctx); + static_assert(dukx_type_traits::value, "type T not supported"); - (void)duk_throw(ctx); + dukx_type_traits::raise(ctx, error); } /** diff -r e81c6badede3 -r a059100b73dc libirccd-js/irccd/js/elapsed_timer_jsapi.cpp --- a/libirccd-js/irccd/js/elapsed_timer_jsapi.cpp Fri Apr 13 22:43:10 2018 +0200 +++ b/libirccd-js/irccd/js/elapsed_timer_jsapi.cpp Fri Apr 13 23:05:56 2018 +0200 @@ -27,6 +27,8 @@ const char* signature("\xff""\xff""irccd-elapsed-timer-ptr"); +// {{{ self + boost::timer::cpu_timer* self(duk_context* ctx) { dukx_stack_assert sa(ctx); @@ -42,34 +44,46 @@ return ptr; } +// }}} + +// {{{ Irccd.ElapsedTimer.prototype.pause + /* - * Method: ElapsedTimer.pause + * Method: ElapsedTimer.prototype.pause * ------------------------------------------------------------------ * * Pause the timer, without resetting the current elapsed time stored. */ -duk_ret_t pause(duk_context* ctx) +duk_ret_t ElapsedTimer_prototype_pause(duk_context* ctx) { self(ctx)->stop(); return 0; } +// }}} + +// {{{ Irccd.ElapsedTimer.prototype.restart + /* - * Method: ElapsedTimer.restart + * Method: Irccd.ElapsedTimer.prototype.restart * ------------------------------------------------------------------ * * Restart the timer without resetting the current elapsed time. */ -duk_ret_t restart(duk_context* ctx) +duk_ret_t ElapsedTimer_prototype_restart(duk_context* ctx) { self(ctx)->resume(); return 0; } +// }}} + +// {{{ Irccd.ElapsedTimer.prototype.elapsed + /* - * Method: ElapsedTimer.elapsed + * Method: ElapsedTimer.prototype.elapsed * ------------------------------------------------------------------ * * Get the number of elapsed milliseconds. @@ -77,20 +91,24 @@ * Returns: * The time elapsed. */ -duk_ret_t elapsed(duk_context* ctx) +duk_ret_t ElapsedTimer_prototype_elapsed(duk_context* ctx) { duk_push_uint(ctx, self(ctx)->elapsed().wall / 1000000LL); return 1; } +// }}} + +// {{{ Irccd.ElapsedTimer [constructor] + /* * Function: Irccd.ElapsedTimer [constructor] * ------------------------------------------------------------------ * * Construct a new ElapsedTimer object. */ -duk_ret_t constructor(duk_context* ctx) +duk_ret_t ElapsedTimer_constructor(duk_context* ctx) { duk_push_this(ctx); duk_push_pointer(ctx, new boost::timer::cpu_timer); @@ -100,13 +118,17 @@ return 0; } +// }}} + +// {{{ Irccd.ElapsedTimer [destructor] + /* * Function: Irccd.ElapsedTimer [destructor] * ------------------------------------------------------------------ * * Delete the property. */ -duk_ret_t destructor(duk_context* ctx) +duk_ret_t ElapsedTimer_destructor(duk_context* ctx) { duk_get_prop_string(ctx, 0, signature); delete static_cast(duk_to_pointer(ctx, -1)); @@ -116,11 +138,13 @@ return 0; } +// }}} + const duk_function_list_entry methods[] = { - { "elapsed", elapsed, 0 }, - { "pause", pause, 0 }, - { "restart", restart, 0 }, - { nullptr, nullptr, 0 } + { "elapsed", ElapsedTimer_prototype_elapsed, 0 }, + { "pause", ElapsedTimer_prototype_pause, 0 }, + { "restart", ElapsedTimer_prototype_restart, 0 }, + { nullptr, nullptr, 0 } }; } // !namespace @@ -135,10 +159,10 @@ dukx_stack_assert sa(plugin->context()); duk_get_global_string(plugin->context(), "Irccd"); - duk_push_c_function(plugin->context(), constructor, 0); + duk_push_c_function(plugin->context(), ElapsedTimer_constructor, 0); duk_push_object(plugin->context()); duk_put_function_list(plugin->context(), -1, methods); - duk_push_c_function(plugin->context(), destructor, 1); + duk_push_c_function(plugin->context(), ElapsedTimer_destructor, 1); duk_set_finalizer(plugin->context(), -2); duk_put_prop_string(plugin->context(), -2, "prototype"); duk_put_prop_string(plugin->context(), -2, "ElapsedTimer"); diff -r e81c6badede3 -r a059100b73dc libirccd-js/irccd/js/file_jsapi.cpp --- a/libirccd-js/irccd/js/file_jsapi.cpp Fri Apr 13 22:43:10 2018 +0200 +++ b/libirccd-js/irccd/js/file_jsapi.cpp Fri Apr 13 23:05:56 2018 +0200 @@ -38,7 +38,7 @@ const char *prototype("\xff""\xff""irccd-file-prototype"); // Remove trailing \r for CRLF line style. -inline std::string clear_crlf(std::string input) +std::string clear_crlf(std::string input) noexcept { if (input.length() > 0 && input.back() == '\r') input.pop_back(); @@ -46,6 +46,11 @@ return input; } +std::system_error from_errno() noexcept +{ + return std::system_error(make_error_code(static_cast(errno))); +} + std::shared_ptr self(duk_context* ctx) { dukx_stack_assert sa(ctx); @@ -61,120 +66,157 @@ return *ptr; } -/* - * File methods. - * ------------------------------------------------------------------ - */ - -/* - * Method: File.basename() - * -------------------------------------------------------- - * - * Synonym of `irccd.File.basename(path)` but with the path from the file. - * - * duk_ret_turns: - * The base name. - */ -duk_ret_t method_basename(duk_context* ctx) +template +duk_ret_t wrap(duk_context* ctx, Handler handler) { - return dukx_push(ctx, fs_util::base_name(self(ctx)->get_path())); -} - -/* - * Method: File.close() - * -------------------------------------------------------- - * - * Force close of the file, automatically called when object is collected. - */ -duk_ret_t method_close(duk_context* ctx) -{ - self(ctx)->close(); + try { + return handler(); + } catch (const boost::system::system_error& ex) { + dukx_throw(ctx, ex); + } catch (const std::system_error& ex) { + dukx_throw(ctx, ex); + } catch (const std::exception& ex) { + dukx_throw(ctx, ex); + } return 0; } +// {{{ Irccd.File.prototype.basename + /* - * Method: File.dirname() + * Method: Irccd.File.prototype.basename() + * -------------------------------------------------------- + * + * Synonym of `Irccd.File.basename(path)` but with the path from the file. + * + * Returns: + * The base name. + */ +duk_ret_t File_prototype_basename(duk_context* ctx) +{ + return wrap(ctx, [&] { + return dukx_push(ctx, fs_util::base_name(self(ctx)->get_path())); + }); +} + +// }}} + +// {{{ Irccd.File.prototype.close + +/* + * Method: Irccd.File.prototype.close() * -------------------------------------------------------- * - * Synonym of `irccd.File.dirname(path)` but with the path from the file. + * Force close of the file, automatically called when object is collected. + */ +duk_ret_t File_prototype_close(duk_context* ctx) +{ + return wrap(ctx, [&] { + self(ctx)->close(); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.File.prototype.dirname + +/* + * Method: Irccd.File.prototype.dirname() + * -------------------------------------------------------- * - * duk_ret_turns: + * Synonym of `Irccd.File.dirname(path)` but with the path from the file. + * + * Returns: * The directory name. */ -duk_ret_t method_dirname(duk_context* ctx) +duk_ret_t File_prototype_dirname(duk_context* ctx) { - return dukx_push(ctx, fs_util::dir_name(self(ctx)->get_path())); + return wrap(ctx, [&] { + return dukx_push(ctx, fs_util::dir_name(self(ctx)->get_path())); + }); } +// }}} + +// {{{ Irccd.File.prototype.lines + /* - * Method: File.lines() + * Method: Irccd.File.prototype.lines() * -------------------------------------------------------- * * Read all lines and return an array. * - * duk_ret_turns: + * Returns: * An array with all lines. * Throws * - Any exception on error. */ -duk_ret_t method_lines(duk_context* ctx) +duk_ret_t File_prototype_lines(duk_context* ctx) { - duk_push_array(ctx); + return wrap(ctx, [&] { + duk_push_array(ctx); + + std::FILE* fp = self(ctx)->get_handle(); + std::string buffer; + std::array data; + std::int32_t i = 0; + + while (std::fgets(&data[0], data.size(), fp) != nullptr) { + buffer += data.data(); + + const auto pos = buffer.find('\n'); - std::FILE* fp = self(ctx)->get_handle(); - std::string buffer; - std::array data; - std::int32_t i = 0; + if (pos != std::string::npos) { + dukx_push(ctx, clear_crlf(buffer.substr(0, pos))); + duk_put_prop_index(ctx, -2, i++); + + buffer.erase(0, pos + 1); + } + } - while (std::fgets(&data[0], data.size(), fp) != nullptr) { - buffer += data.data(); + // Maybe an error in the stream. + if (std::ferror(fp)) + throw from_errno(); - const auto pos = buffer.find('\n'); - - if (pos != std::string::npos) { - dukx_push(ctx, clear_crlf(buffer.substr(0, pos))); + // Missing '\n' in end of file. + if (!buffer.empty()) { + dukx_push(ctx, clear_crlf(buffer)); duk_put_prop_index(ctx, -2, i++); - - buffer.erase(0, pos + 1); } - } - // Maybe an error in the stream. - if (std::ferror(fp)) - dukx_throw(ctx, system_error()); - - // Missing '\n' in end of file. - if (!buffer.empty()) { - dukx_push(ctx, clear_crlf(buffer)); - duk_put_prop_index(ctx, -2, i++); - } - - return 1; + return 1; + }); } +// }}} + +// {{{ Irccd.File.prototype.read + /* - * Method: File.read(amount) + * Method: Irccd.File.prototype.read(amount) * -------------------------------------------------------- * * Read the specified amount of characters or the whole file. * * Arguments: * - amount, the amount of characters or -1 to read all (Optional, default: -1). - * duk_ret_turns: + * Returns: * The string. * Throws: - * - Any exception on error. + * - Irccd.SystemError on errors */ -duk_ret_t method_read(duk_context* ctx) +duk_ret_t File_prototype_read(duk_context* ctx) { - const auto fp = self(ctx)->get_handle(); - const auto amount = duk_is_number(ctx, 0) ? duk_get_int(ctx, 0) : -1; + return wrap(ctx, [&] { + const auto fp = self(ctx)->get_handle(); + const auto amount = duk_is_number(ctx, 0) ? duk_get_int(ctx, 0) : -1; - if (amount == 0 || !fp) - return 0; + if (amount == 0 || !fp) + return 0; - try { std::string data; std::size_t total = 0; @@ -184,7 +226,7 @@ while ((nread = std::fread(&buffer[0], sizeof (buffer[0]), buffer.size(), fp)) > 0) { if (std::ferror(fp)) - dukx_throw(ctx, system_error()); + throw from_errno(); std::copy(buffer.begin(), buffer.begin() + nread, std::back_inserter(data)); total += nread; @@ -194,66 +236,77 @@ total = std::fread(&data[0], sizeof (data[0]), static_cast(amount), fp); if (std::ferror(fp)) - dukx_throw(ctx, system_error()); + throw from_errno(); data.resize(total); } - dukx_push(ctx, data); - } catch (const std::exception&) { - dukx_throw(ctx, system_error()); - } - - return 1; + return dukx_push(ctx, data); + }); } +// }}} + +// {{{ Irccd.File.prototype.readline + /* - * Method: File.readline() + * Method: Irccd.File.prototype.readline() * -------------------------------------------------------- * * Read the next line available. * - * duk_ret_turns: + * Returns: * The next line or undefined if eof. * Throws: - * - Any exception on error. + * - Irccd.SystemError on errors */ -duk_ret_t method_readline(duk_context* ctx) +duk_ret_t File_prototype_readline(duk_context* ctx) { - auto fp = self(ctx)->get_handle(); + return wrap(ctx, [&] { + auto fp = self(ctx)->get_handle(); - if (fp == nullptr || std::feof(fp)) - return 0; + if (fp == nullptr || std::feof(fp)) + return 0; - std::string result; + std::string result; - for (int ch; (ch = std::fgetc(fp)) != EOF && ch != '\n'; ) - result += (char)ch; - if (std::ferror(fp)) - dukx_throw(ctx, system_error()); + for (int ch; (ch = std::fgetc(fp)) != EOF && ch != '\n'; ) + result += (char)ch; + if (std::ferror(fp)) + throw from_errno(); - return dukx_push(ctx, clear_crlf(result)); + return dukx_push(ctx, clear_crlf(result)); + }); } +// }}} + +// {{{ Irccd.File.prototype.remove + /* - * Method: File.remove() + * Method: Irccd.File.prototype.remove() * -------------------------------------------------------- * - * Synonym of File.remove(path) but with the path from the file. + * Synonym of Irccd.File.prototype.remove(path) but with the path from the file. * * Throws: - * - Any exception on error. + * - Irccd.SystemError on errors */ -duk_ret_t method_remove(duk_context* ctx) +duk_ret_t File_prototype_remove(duk_context* ctx) { - if (::remove(self(ctx)->get_path().c_str()) < 0) - dukx_throw(ctx, system_error()); + return wrap(ctx, [&] { + boost::filesystem::remove(self(ctx)->get_path()); - return 0; + return 0; + }); } +// }}} + +// {{{ Irccd.File.prototype.seek + /* - * Method: File.seek(type, amount) + * Method: Irccd.File.prototype.seek(type, amount) * -------------------------------------------------------- * * Sets the position in the file. @@ -262,127 +315,129 @@ * - type, the type of setting (File.SeekSet, File.SeekCur, File.SeekSet), * - amount, the new offset. * Throws: - * - Any exception on error. + * - Irccd.SystemError on errors */ -duk_ret_t method_seek(duk_context* ctx) +duk_ret_t File_prototype_seek(duk_context* ctx) { - auto fp = self(ctx)->get_handle(); - auto type = duk_require_int(ctx, 0); - auto amount = duk_require_int(ctx, 1); + return wrap(ctx, [&] { + auto fp = self(ctx)->get_handle(); + auto type = duk_require_int(ctx, 0); + auto amount = duk_require_int(ctx, 1); + + if (fp != nullptr && std::fseek(fp, amount, type) != 0) + throw from_errno(); - if (fp != nullptr && std::fseek(fp, amount, type) != 0) - dukx_throw(ctx, system_error()); + return 0; + }); +} - return 0; -} +// }}} + +// {{{ Irccd.File.prototype.stat #if defined(HAVE_STAT) /* - * Method: File.stat() [optional] + * Method: Irccd.File.prototype.stat() [optional] * -------------------------------------------------------- * * Synonym of File.stat(path) but with the path from the file. * - * duk_ret_turns: + * Returns: * The stat information. * Throws: - * - Any exception on error. + * - Irccd.SystemError on errors */ -duk_ret_t method_stat(duk_context* ctx) +duk_ret_t File_prototype_stat(duk_context* ctx) { - auto file = self(ctx); - struct stat st; + return wrap(ctx, [&] { + auto file = self(ctx); + struct stat st; - if (file->get_handle() == nullptr && ::stat(file->get_path().c_str(), &st) < 0) - dukx_throw(ctx, system_error()); - else + if (file->get_handle() == nullptr && ::stat(file->get_path().c_str(), &st) < 0) + throw from_errno(); + dukx_push(ctx, st); - return 1; + return 1; + }); } #endif // !HAVE_STAT +// }}} + +// {{{ Irccd.File.prototype.tell + /* - * Method: File.tell() + * Method: Irccd.File.prototype.tell() * -------------------------------------------------------- * * Get the actual position in the file. * - * duk_ret_turns: + * Returns: * The position. * Throws: - * - Any exception on error. + * - Irccd.SystemError on errors */ -duk_ret_t method_tell(duk_context* ctx) +duk_ret_t File_prototype_tell(duk_context* ctx) { - auto fp = self(ctx)->get_handle(); - long pos; + return wrap(ctx, [&] { + auto fp = self(ctx)->get_handle(); + long pos; - if (fp == nullptr) - return 0; + if (fp == nullptr) + return 0; - if ((pos = std::ftell(fp)) == -1L) - dukx_throw(ctx, system_error()); - else + if ((pos = std::ftell(fp)) == -1L) + throw from_errno(); + duk_push_int(ctx, pos); - return 1; + return 1; + }); } +// }}} + +// {{{ Irccd.File.prototype.write + /* - * Method: File.write(data) + * Method: Irccd.File.prototype.write(data) * -------------------------------------------------------- * * Write some characters to the file. * * Arguments: * - data, the character to write. - * duk_ret_turns: + * Returns: * The number of bytes written. * Throws: - * - Any exception on error. + * - Irccd.SystemError on errors */ -duk_ret_t method_write(duk_context* ctx) +duk_ret_t File_prototype_write(duk_context* ctx) { - auto fp = self(ctx)->get_handle(); - auto data = dukx_require(ctx, 0); + return wrap(ctx, [&] { + auto fp = self(ctx)->get_handle(); + auto data = dukx_require(ctx, 0); - if (fp == nullptr) - return 0; + if (fp == nullptr) + return 0; - const auto nwritten = std::fwrite(data.c_str(), 1, data.length(), fp); + const auto nwritten = std::fwrite(data.c_str(), 1, data.length(), fp); - if (std::ferror(fp)) - dukx_throw(ctx, system_error()); + if (std::ferror(fp)) + throw from_errno(); - duk_push_uint(ctx, nwritten); + duk_push_uint(ctx, nwritten); - return 1; + return 1; + }); } -const duk_function_list_entry methods[] = { - { "basename", method_basename, 0 }, - { "close", method_close, 0 }, - { "dirname", method_dirname, 0 }, - { "lines", method_lines, 0 }, - { "read", method_read, 1 }, - { "readline", method_readline, 0 }, - { "remove", method_remove, 0 }, - { "seek", method_seek, 2 }, -#if defined(HAVE_STAT) - { "stat", method_stat, 0 }, -#endif - { "tell", method_tell, 0 }, - { "write", method_write, 1 }, - { nullptr, nullptr, 0 } -}; +// }}} -/* - * File "static" functions - * ------------------------------------------------------------------ - */ +// {{{ Irccd.File [constructor] /* * Function: Irccd.File(path, mode) [constructor] @@ -394,35 +449,37 @@ * - path, the path to the file, * - mode, the mode string. * Throws: - * - Any exception on error. + * - Irccd.SystemError on errors */ -duk_ret_t constructor(duk_context* ctx) +duk_ret_t File_constructor(duk_context* ctx) { - if (!duk_is_constructor_call(ctx)) - return 0; + return wrap(ctx, [&] { + if (!duk_is_constructor_call(ctx)) + return 0; - try { - auto path = dukx_require(ctx, 0); - auto mode = dukx_require(ctx, 1); + const auto path = dukx_require(ctx, 0); + const auto mode = dukx_require(ctx, 1); duk_push_this(ctx); duk_push_pointer(ctx, new std::shared_ptr(new file(path, mode))); duk_put_prop_string(ctx, -2, signature); duk_pop(ctx); - } catch (const std::exception&) { - dukx_throw(ctx, system_error()); - } - return 0; + return 0; + }); } +// }}} + +// {{{ Irccd.File [destructor] + /* * Function: Irccd.File() [destructor] * ------------------------------------------------------------------ * * Delete the property. */ -duk_ret_t destructor(duk_context* ctx) +duk_ret_t File_destructor(duk_context* ctx) { duk_get_prop_string(ctx, 0, signature); delete static_cast*>(duk_to_pointer(ctx, -1)); @@ -432,6 +489,10 @@ return 0; } +// }}} + +// {{{ Irccd.File.basename + /* * Function: Irccd.File.basename(path) * -------------------------------------------------------- @@ -440,14 +501,22 @@ * * Arguments: * - path, the path to the file. - * duk_ret_turns: + * Returns: * The base name. + * Throws: + * - Irccd.SystemError on errors */ -duk_ret_t function_basename(duk_context* ctx) +duk_ret_t File_basename(duk_context* ctx) { - return dukx_push(ctx, fs_util::base_name(duk_require_string(ctx, 0))); + return wrap(ctx, [&] { + return dukx_push(ctx, fs_util::base_name(duk_require_string(ctx, 0))); + }); } +// }}} + +// {{{ Irccd.File.dirname + /* * Function: Irccd.File.dirname(path) * -------------------------------------------------------- @@ -456,14 +525,20 @@ * * Arguments: * - path, the path to the file. - * duk_ret_turns: - * The directory name. + * Throws: + * - Irccd.SystemError on errors */ -duk_ret_t function_dirname(duk_context* ctx) +duk_ret_t File_dirname(duk_context* ctx) { - return dukx_push(ctx, fs_util::dir_name(duk_require_string(ctx, 0))); + return wrap(ctx, [&] { + return dukx_push(ctx, fs_util::dir_name(duk_require_string(ctx, 0))); + }); } +// }}} + +// {{{ Irccd.File.exists + /* * Function: Irccd.File.exists(path) * -------------------------------------------------------- @@ -472,21 +547,21 @@ * * Arguments: * - path, the path to the file. - * duk_ret_turns: + * Returns: * True if exists. * Throws: - * - Any exception if we don't have access. + * - Irccd.SystemError on errors */ -duk_ret_t function_exists(duk_context* ctx) +duk_ret_t File_exists(duk_context* ctx) { - try { - duk_push_boolean(ctx, boost::filesystem::exists(duk_require_string(ctx, 0))); - } catch (...) { - duk_push_boolean(ctx, false); - } + return wrap(ctx, [&] { + return dukx_push(ctx, boost::filesystem::exists(duk_require_string(ctx, 0))); + }); +} - return 1; -} +// }}} + +// {{{ Irccd.File.remove /* * function Irccd.File.remove(path) @@ -497,16 +572,21 @@ * Arguments: * - path, the path to the file. * Throws: - * - Any exception on error. + * - Irccd.SystemError on errors */ -duk_ret_t function_remove(duk_context* ctx) +duk_ret_t File_remove(duk_context* ctx) { - if (::remove(duk_require_string(ctx, 0)) < 0) - dukx_throw(ctx, system_error()); + return wrap(ctx, [&] { + boost::filesystem::remove(dukx_require(ctx, 0)); - return 0; + return 0; + }); } +// }}} + +// {{{ Irccd.File.stat + #if defined(HAVE_STAT) /* @@ -517,32 +597,53 @@ * * Arguments: * - path, the path to the file. - * duk_ret_turns: + * Returns: * The stat information. * Throws: - * - Any exception on error. + * - Irccd.SystemError on errors */ -duk_ret_t function_stat(duk_context* ctx) +duk_ret_t File_stat(duk_context* ctx) { - struct stat st; + return wrap(ctx, [&] { + struct stat st; - if (::stat(duk_require_string(ctx, 0), &st) < 0) - dukx_throw(ctx, system_error()); + if (::stat(duk_require_string(ctx, 0), &st) < 0) + throw from_errno(); - return dukx_push(ctx, st); + return dukx_push(ctx, st); + }); } #endif // !HAVE_STAT +// }}} + +const duk_function_list_entry methods[] = { + { "basename", File_prototype_basename, 0 }, + { "close", File_prototype_close, 0 }, + { "dirname", File_prototype_dirname, 0 }, + { "lines", File_prototype_lines, 0 }, + { "read", File_prototype_read, 1 }, + { "readline", File_prototype_readline, 0 }, + { "remove", File_prototype_remove, 0 }, + { "seek", File_prototype_seek, 2 }, +#if defined(HAVE_STAT) + { "stat", File_prototype_stat, 0 }, +#endif + { "tell", File_prototype_tell, 0 }, + { "write", File_prototype_write, 1 }, + { nullptr, nullptr, 0 } +}; + const duk_function_list_entry functions[] = { - { "basename", function_basename, 1 }, - { "dirname", function_dirname, 1 }, - { "exists", function_exists, 1 }, - { "remove", function_remove, 1 }, + { "basename", File_basename, 1 }, + { "dirname", File_dirname, 1 }, + { "exists", File_exists, 1 }, + { "remove", File_remove, 1 }, #if defined(HAVE_STAT) - { "stat", function_stat, 1 }, + { "stat", File_stat, 1 }, #endif - { nullptr, nullptr, 0 } + { nullptr, nullptr, 0 } }; const duk_number_list_entry constants[] = { @@ -564,12 +665,12 @@ dukx_stack_assert sa(plugin->context()); duk_get_global_string(plugin->context(), "Irccd"); - duk_push_c_function(plugin->context(), constructor, 2); + duk_push_c_function(plugin->context(), File_constructor, 2); duk_put_number_list(plugin->context(), -1, constants); duk_put_function_list(plugin->context(), -1, functions); duk_push_object(plugin->context()); duk_put_function_list(plugin->context(), -1, methods); - duk_push_c_function(plugin->context(), destructor, 1); + duk_push_c_function(plugin->context(), File_destructor, 1); duk_set_finalizer(plugin->context(), -2); duk_dup(plugin->context(), -1); duk_put_global_string(plugin->context(), prototype); diff -r e81c6badede3 -r a059100b73dc libirccd-js/irccd/js/irccd_jsapi.cpp --- a/libirccd-js/irccd/js/irccd_jsapi.cpp Fri Apr 13 22:43:10 2018 +0200 +++ b/libirccd-js/irccd/js/irccd_jsapi.cpp Fri Apr 13 23:05:56 2018 +0200 @@ -29,6 +29,21 @@ namespace { +template +void do_raise(duk_context* ctx, const Error& ex) +{ + dukx_stack_assert sa(ctx, 1); + + duk_get_global_string(ctx, "Irccd"); + duk_get_prop_string(ctx, -1, "SystemError"); + duk_remove(ctx, -2); + dukx_push(ctx, ex.code().value()); + dukx_push(ctx, ex.code().message()); + duk_new(ctx, 2); + + (void)duk_throw(ctx); +} + const std::unordered_map errors{ { "E2BIG", E2BIG }, { "EACCES", EACCES }, @@ -146,28 +161,14 @@ } // !namespace -system_error::system_error() - : errno_(errno) - , message_(std::strerror(errno_)) +void dukx_type_traits::raise(duk_context* ctx, const std::system_error& ex) { + do_raise(ctx, ex); } -system_error::system_error(int e, std::string message) - : errno_(e) - , message_(std::move(message)) -{ -} - -void system_error::create(duk_context *ctx) const +void dukx_type_traits::raise(duk_context* ctx, const boost::system::system_error& ex) { - dukx_stack_assert sa(ctx, 1); - - duk_get_global_string(ctx, "Irccd"); - duk_get_prop_string(ctx, -1, "SystemError"); - duk_remove(ctx, -2); - dukx_push(ctx, errno_); - dukx_push(ctx, message_); - duk_new(ctx, 2); + do_raise(ctx, ex); } std::string irccd_jsapi::get_name() const diff -r e81c6badede3 -r a059100b73dc libirccd-js/irccd/js/irccd_jsapi.hpp --- a/libirccd-js/irccd/js/irccd_jsapi.hpp Fri Apr 13 22:43:10 2018 +0200 +++ b/libirccd-js/irccd/js/irccd_jsapi.hpp Fri Apr 13 23:05:56 2018 +0200 @@ -27,42 +27,15 @@ #include #include #include +#include + +#include #include "jsapi.hpp" namespace irccd { /** - * \brief Custom Javascript exception for system error. - */ -class system_error { -private: - int errno_; - std::string message_; - -public: - /** - * Create a system error from the current errno value. - */ - system_error(); - - /** - * Create a system error with the given errno and message. - * - * \param e the errno number - * \param message the message - */ - system_error(int e, std::string message); - - /** - * Create the SystemError Javascript exception. - * - * \param ctx the context - */ - void create(duk_context* ctx) const; -}; - -/** * \brief Irccd Javascript API. * \ingroup jsapi */ @@ -94,6 +67,36 @@ static irccd& self(duk_context* ctx); }; +/** + * \brief Specialize dukx_type_traits for std::system_error. + */ +template <> +class dukx_type_traits : public std::true_type { +public: + /** + * Raise an Irccd.SystemError. + * + * \param ctx the context + * param ex the exception + */ + static void raise(duk_context* ctx, const std::system_error& ex); +}; + +/** + * \brief Specialize dukx_type_traits for boost::system::system_error. + */ +template <> +class dukx_type_traits : public std::true_type { +public: + /** + * Raise an Irccd.SystemError. + * + * \param ctx the context + * param ex the exception + */ + static void raise(duk_context* ctx, const boost::system::system_error& ex); +}; + } // !irccd #endif // !IRCCD_JS_IRCCD_JSAPI_HPP diff -r e81c6badede3 -r a059100b73dc libirccd-js/irccd/js/logger_jsapi.cpp --- a/libirccd-js/irccd/js/logger_jsapi.cpp Fri Apr 13 22:43:10 2018 +0200 +++ b/libirccd-js/irccd/js/logger_jsapi.cpp Fri Apr 13 23:05:56 2018 +0200 @@ -28,15 +28,24 @@ namespace { +// {{{ print + duk_ret_t print(duk_context* ctx, std::ostream &out) { - const auto plugin = dukx_type_traits::self(ctx); - - out << "plugin " << plugin->get_name() << ": " << duk_require_string(ctx, 0) << std::endl; + try { + out << "plugin " << dukx_type_traits::self(ctx)->get_name() << ": "; + out << duk_require_string(ctx, 0) << std::endl; + } catch (const std::exception& ex) { + dukx_throw(ctx, ex); + } return 0; } +// }}} + +// {{{ Irccd.Logger.info + /* * Function: Irccd.Logger.info(message) * -------------------------------------------------------- @@ -45,45 +54,61 @@ * * Arguments: * - message, the message. + * Throws: + * - Irccd.SystemError on errors */ -duk_ret_t info(duk_context* ctx) +duk_ret_t Logger_info(duk_context* ctx) { return print(ctx, dukx_type_traits::self(ctx).get_log().info()); } +// }}} + +// {{{ Irccd.Logger.warning + /* - * Function: irccd.Logger.warning(message) + * Function: Irccd.Logger.warning(message) * -------------------------------------------------------- * * Write a warning message. * * Arguments: * - message, the warning. + * Throws: + * - Irccd.SystemError on errors */ -duk_ret_t warning(duk_context* ctx) +duk_ret_t Logger_warning(duk_context* ctx) { return print(ctx, dukx_type_traits::self(ctx).get_log().warning()); } +// }}} + +// {{{ Irccd.Logger.debug + /* - * Function: Logger.debug(message) + * Function: Irccd.Logger.debug(message) * -------------------------------------------------------- * * Write a debug message, only shown if irccd is compiled in debug. * * Arguments: * - message, the message. + * Throws: + * - Irccd.SystemError on errors */ -duk_ret_t debug(duk_context* ctx) +duk_ret_t Logger_debug(duk_context* ctx) { return print(ctx, dukx_type_traits::self(ctx).get_log().debug()); } +// }}} + const duk_function_list_entry functions[] = { - { "info", info, 1 }, - { "warning", warning, 1 }, - { "debug", debug, 1 }, - { nullptr, nullptr, 0 } + { "info", Logger_info, 1 }, + { "warning", Logger_warning, 1 }, + { "debug", Logger_debug, 1 }, + { nullptr, nullptr, 0 } }; } // !namespace diff -r e81c6badede3 -r a059100b73dc libirccd-js/irccd/js/plugin_jsapi.cpp --- a/libirccd-js/irccd/js/plugin_jsapi.cpp Fri Apr 13 22:43:10 2018 +0200 +++ b/libirccd-js/irccd/js/plugin_jsapi.cpp Fri Apr 13 23:05:56 2018 +0200 @@ -30,30 +30,20 @@ const char plugin_ref[] = "\xff""\xff""irccd-plugin-ptr"; -/* - * wrap - * ------------------------------------------------------------------ - * - * Wrap function for these functions because they all takes the same arguments. - * - * - load, - * - reload, - * - unload. - */ -template -duk_idx_t wrap(duk_context* ctx, int nret, Func&& func) +template +duk_idx_t wrap(duk_context* ctx, Handler handler) { - std::string name = duk_require_string(ctx, 0); - try { - func(dukx_type_traits::self(ctx), name); - } catch (const std::out_of_range& ex) { - (void)duk_error(ctx, DUK_ERR_REFERENCE_ERROR, "%s", ex.what()); - } catch (const std::exception &ex) { - (void)duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what()); + return handler(); + } catch (const plugin_error& ex) { + dukx_throw(ctx, ex); + } catch (const std::system_error& ex) { + dukx_throw(ctx, ex); + } catch (const std::exception& ex) { + dukx_throw(ctx, ex); } - return nret; + return 0; } /* @@ -190,6 +180,8 @@ return get(ctx, js_plugin::paths_property); } +// {{{ Irccd.Plugin.info + /* * Function: Irccd.Plugin.info([name]) * ------------------------------------------------------------------ @@ -209,34 +201,42 @@ * selected. * Returns: * The plugin information or undefined if the plugin was not found. + * Throws: + * - Irccd.SystemError on errors. */ -duk_idx_t info(duk_context* ctx) +duk_idx_t Plugin_info(duk_context* ctx) { - std::shared_ptr plugin; + return wrap(ctx, [&] { + std::shared_ptr plugin; - if (duk_get_top(ctx) >= 1) - plugin = dukx_type_traits::self(ctx).plugins().get(duk_require_string(ctx, 0)); - else - plugin = dukx_type_traits::self(ctx); + if (duk_get_top(ctx) >= 1) + plugin = dukx_type_traits::self(ctx).plugins().get(duk_require_string(ctx, 0)); + else + plugin = dukx_type_traits::self(ctx); - if (!plugin) - return 0; + if (!plugin) + return 0; - duk_push_object(ctx); - dukx_push(ctx, plugin->get_name()); - duk_put_prop_string(ctx, -2, "name"); - dukx_push(ctx, plugin->get_author()); - duk_put_prop_string(ctx, -2, "author"); - dukx_push(ctx, plugin->get_license()); - duk_put_prop_string(ctx, -2, "license"); - dukx_push(ctx, plugin->get_summary()); - duk_put_prop_string(ctx, -2, "summary"); - dukx_push(ctx, plugin->get_version()); - duk_put_prop_string(ctx, -2, "version"); + duk_push_object(ctx); + dukx_push(ctx, plugin->get_name()); + duk_put_prop_string(ctx, -2, "name"); + dukx_push(ctx, plugin->get_author()); + duk_put_prop_string(ctx, -2, "author"); + dukx_push(ctx, plugin->get_license()); + duk_put_prop_string(ctx, -2, "license"); + dukx_push(ctx, plugin->get_summary()); + duk_put_prop_string(ctx, -2, "summary"); + dukx_push(ctx, plugin->get_version()); + duk_put_prop_string(ctx, -2, "version"); - return 1; + return 1; + }); } +// }}} + +// {{{ Irccd.Plugin.list + /* * Function: Irccd.Plugin.list() * ------------------------------------------------------------------ @@ -246,7 +246,7 @@ * Returns: * The list of all plugin names. */ -duk_idx_t list(duk_context* ctx) +duk_idx_t Plugin_list(duk_context* ctx) { int i = 0; @@ -260,6 +260,10 @@ return 1; } +// }}} + +// {{{ Irccd.Plugin.load + /* * Function: Irccd.Plugin.load(name) * ------------------------------------------------------------------ @@ -270,16 +274,22 @@ * Arguments: * - name, the plugin identifier. * Throws: - * - Error on errors, - * - ReferenceError if the plugin was not found. + * - Irccd.PluginError on plugin related errors, + * - Irccd.SystemError on other errors. */ -duk_idx_t load(duk_context* ctx) +duk_idx_t Plugin_load(duk_context* ctx) { - return wrap(ctx, 0, [&] (irccd& irccd, const std::string& name) { - irccd.plugins().load(name); + return wrap(ctx, [&] { + dukx_type_traits::self(ctx).plugins().load(dukx_require(ctx, 0)); + + return 0; }); } +// }}} + +// {{{ Irccd.Plugin.reload + /* * Function: Irccd.Plugin.reload(name) * ------------------------------------------------------------------ @@ -289,16 +299,22 @@ * Arguments: * - name, the plugin identifier. * Throws: - * - Error on errors, - * - ReferenceError if the plugin was not found. + * - Irccd.PluginError on plugin related errors, + * - Irccd.SystemError on other errors. */ -duk_idx_t reload(duk_context* ctx) +duk_idx_t Plugin_reload(duk_context* ctx) { - return wrap(ctx, 0, [&] (irccd& irccd, const std::string& name) { - irccd.plugins().reload(name); + return wrap(ctx, [&] { + dukx_type_traits::self(ctx).plugins().reload(dukx_require(ctx, 0)); + + return 0; }); } +// }}} + +// {{{ Irccd.Plugin.unload + /* * Function: Irccd.Plugin.unload(name) * ------------------------------------------------------------------ @@ -308,23 +324,55 @@ * Arguments: * - name, the plugin identifier. * Throws: - * - Error on errors, - * - ReferenceError if the plugin was not found. + * - Irccd.PluginError on plugin related errors, + * - Irccd.SystemError on other errors. */ -duk_idx_t unload(duk_context* ctx) +duk_idx_t Plugin_unload(duk_context* ctx) { - return wrap(ctx, 0, [&] (irccd &irccd, const std::string &name) { - irccd.plugins().unload(name); + return wrap(ctx, [&] { + dukx_type_traits::self(ctx).plugins().unload(dukx_require(ctx, 0)); + + return 0; }); } +// }}} + +// {{{ Irccd.PluginError [constructor] + +/* + * Function: Irccd.PluginError(code, message) + * ------------------------------------------------------------------ + * + * Create an Irccd.PluginError object. + * + * Arguments: + * - code, the error code, + * - message, the error message. + */ +duk_ret_t PluginError_constructor(duk_context* ctx) +{ + duk_push_this(ctx); + duk_push_int(ctx, duk_require_int(ctx, 0)); + duk_put_prop_string(ctx, -2, "code"); + duk_push_string(ctx, duk_require_string(ctx, 1)); + duk_put_prop_string(ctx, -2, "message"); + duk_push_string(ctx, "PluginError"); + duk_put_prop_string(ctx, -2, "name"); + duk_pop(ctx); + + return 0; +} + +// }}} + const duk_function_list_entry functions[] = { - { "info", info, DUK_VARARGS }, - { "list", list, 0 }, - { "load", load, 1 }, - { "reload", reload, 1 }, - { "unload", unload, 1 }, - { nullptr, nullptr, 0 } + { "info", Plugin_info, DUK_VARARGS }, + { "list", Plugin_list, 0 }, + { "load", Plugin_load, 1 }, + { "reload", Plugin_reload, 1 }, + { "unload", Plugin_unload, 1 }, + { nullptr, nullptr, 0 } }; } // !namespace @@ -373,10 +421,23 @@ duk_push_c_function(plugin->context(), set_paths, 1); duk_def_prop(plugin->context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); + // PluginError function. + duk_push_c_function(plugin->context(), PluginError_constructor, 2); + duk_push_object(plugin->context()); + duk_get_global_string(plugin->context(), "Error"); + duk_get_prop_string(plugin->context(), -1, "prototype"); + duk_remove(plugin->context(), -2); + duk_set_prototype(plugin->context(), -2); + duk_put_prop_string(plugin->context(), -2, "prototype"); + duk_put_prop_string(plugin->context(), -2, "PluginError"); + duk_put_prop_string(plugin->context(), -2, "Plugin"); duk_pop(plugin->context()); } +using plugin_traits = dukx_type_traits; +using plugin_error_traits = dukx_type_traits; + std::shared_ptr dukx_type_traits::self(duk_context* ctx) { dukx_stack_assert sa(ctx); @@ -388,4 +449,18 @@ return plugin->lock(); } +void plugin_error_traits::raise(duk_context* ctx, const plugin_error& ex) +{ + dukx_stack_assert sa(ctx, 1); + + duk_get_global_string(ctx, "Irccd"); + duk_get_prop_string(ctx, -1, "PluginError"); + duk_remove(ctx, -2); + dukx_push(ctx, ex.code().value()); + dukx_push(ctx, ex.code().message()); + duk_new(ctx, 2); + + (void)duk_throw(ctx); +} + } // !irccd diff -r e81c6badede3 -r a059100b73dc libirccd-js/irccd/js/plugin_jsapi.hpp --- a/libirccd-js/irccd/js/plugin_jsapi.hpp Fri Apr 13 22:43:10 2018 +0200 +++ b/libirccd-js/irccd/js/plugin_jsapi.hpp Fri Apr 13 23:05:56 2018 +0200 @@ -25,6 +25,7 @@ */ #include "jsapi.hpp" +#include "js_plugin.hpp" namespace irccd { @@ -60,6 +61,21 @@ static std::shared_ptr self(duk_context* ctx); }; +/** + * \brief Specialization for plugin_error. + */ +template <> +class dukx_type_traits : public std::true_type { +public: + /** + * Raise a plugin_error. + * + * \param ctx the context + * \param error the error + */ + static void raise(duk_context* ctx, const plugin_error& error); +}; + } // !irccd #endif // !IRCCD_JS_PLUGIN_JSAPI_HPP diff -r e81c6badede3 -r a059100b73dc libirccd-js/irccd/js/server_jsapi.cpp --- a/libirccd-js/irccd/js/server_jsapi.cpp Fri Apr 13 22:43:10 2018 +0200 +++ b/libirccd-js/irccd/js/server_jsapi.cpp Fri Apr 13 23:05:56 2018 +0200 @@ -52,8 +52,24 @@ return *static_cast*>(ptr); } +template +duk_ret_t wrap(duk_context* ctx, Handler handler) +{ + try { + return handler(ctx); + } catch (const server_error& ex) { + dukx_throw(ctx, ex); + } catch (const std::exception& ex) { + dukx_throw(ctx, ex); + } + + return 0; +} + +// {{{ Irccd.Server.prototype.info + /* - * Method: Server.info() + * Method: Irccd.Server.prototype.info() * ------------------------------------------------------------------ * * Get the server information as an object containing the following properties: @@ -65,9 +81,9 @@ * sslVerify: true if ssl was verified * channels: an array of all channels */ -duk_ret_t info(duk_context* ctx) +duk_ret_t Server_prototype_info(duk_context* ctx) { - auto server = self(ctx); + const auto server = self(ctx); duk_push_object(ctx); dukx_push(ctx, server->get_name()); @@ -94,8 +110,12 @@ return 1; } +// }}} + +// {{{ Irccd.Server.prototype.invite + /* - * Method: Server.invite(target, channel) + * Method: Irccd.Server.prototype.invite(target, channel) * ------------------------------------------------------------------ * * Invite someone to a channel. @@ -103,25 +123,55 @@ * Arguments: * - target, the target to invite, * - channel, the channel. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. */ -duk_ret_t invite(duk_context* ctx) +duk_ret_t Server_prototype_invite(duk_context* ctx) { - self(ctx)->invite(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); + return wrap(ctx, [] (auto ctx) { + auto target = dukx_require(ctx, 0); + auto channel = dukx_require(ctx, 1); + + if (target.empty()) + throw server_error(server_error::invalid_nickname); + if (channel.empty()) + throw server_error(server_error::invalid_channel); - return 0; + self(ctx)->invite(std::move(target), std::move(channel)); + + return 0; + }); } +// }}} + +// {{{ Irccd.Server.prototype.isSelf + /* - * Method: Server.isSelf(nickname) + * Method: Irccd.Server.prototype.isSelf(nickname) * ------------------------------------------------------------------ + * + * Arguments: + * - nickname, the nickname to check. + * Returns: + * True if the nickname targets this server. + * Throws: + * - Irccd.SystemError on errors. */ -duk_ret_t isSelf(duk_context* ctx) +duk_ret_t Server_prototype_isSelf(duk_context* ctx) { - return dukx_push(ctx, self(ctx)->is_self(duk_require_string(ctx, 0))); + return wrap(ctx, [] (auto ctx) { + return dukx_push(ctx, self(ctx)->is_self(dukx_require(ctx, 0))); + }); } +// }}} + +// {{{ Irccd.Server.prototype.join + /* - * Method: Server.join(channel, password = undefined) + * Method: Irccd.Server.prototype.join(channel, password = undefined) * ------------------------------------------------------------------ * * Join a channel with an optional password. @@ -129,16 +179,31 @@ * Arguments: * - channel, the channel to join, * - password, the password or undefined to not use. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. */ -duk_ret_t join(duk_context* ctx) +duk_ret_t Server_prototype_join(duk_context* ctx) { - self(ctx)->join(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); + return wrap(ctx, [] (auto ctx) { + auto channel = dukx_require(ctx, 0); + auto password = dukx_get(ctx, 1); - return 0; + if (channel.empty()) + throw server_error(server_error::invalid_channel); + + self(ctx)->join(std::move(channel), std::move(password)); + + return 0; + }); } +// }}} + +// {{{ Irccd.Server.prototype.kick + /* - * Method: Server.kick(target, channel, reason = undefined) + * Method: Irccd.Server.prototype.kick(target, channel, reason = undefined) * ------------------------------------------------------------------ * * Kick someone from a channel. @@ -147,16 +212,34 @@ * - target, the target to kick, * - channel, the channel, * - reason, the optional reason or undefined to not set. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. */ -duk_ret_t kick(duk_context* ctx) +duk_ret_t Server_prototype_kick(duk_context* ctx) { - self(ctx)->kick(duk_require_string(ctx, 0), duk_require_string(ctx, 1), duk_get_string(ctx, 2)); + return wrap(ctx, [] (auto ctx) { + auto target = dukx_require(ctx, 0); + auto channel = dukx_require(ctx, 1); + auto reason = dukx_get(ctx, 2); - return 0; + if (target.empty()) + throw server_error(server_error::invalid_nickname); + if (channel.empty()) + throw server_error(server_error::invalid_channel); + + self(ctx)->kick(std::move(target), std::move(channel), std::move(reason)); + + return 0; + }); } +// }}} + +// {{{ Irccd.Server.prototype.me + /* - * Method: Server.me(target, message) + * Method: Irccd.Server.prototype.me(target, message) * ------------------------------------------------------------------ * * Send a CTCP Action. @@ -164,16 +247,31 @@ * Arguments: * - target, the target or a channel, * - message, the message. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. */ -duk_ret_t me(duk_context* ctx) +duk_ret_t Server_prototype_me(duk_context* ctx) { - self(ctx)->me(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); + return wrap(ctx, [] (auto ctx) { + auto target = dukx_require(ctx, 0); + auto message = dukx_get(ctx, 1); - return 0; + if (target.empty()) + throw server_error(server_error::invalid_nickname); + + self(ctx)->me(std::move(target), std::move(message)); + + return 0; + }); } +// }}} + +// {{{ Irccd.Server.prototype.message + /* - * Method: Server.message(target, message) + * Method: Irccd.Server.prototype.message(target, message) * ------------------------------------------------------------------ * * Send a message. @@ -181,70 +279,133 @@ * Arguments: * - target, the target or a channel, * - message, the message. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. */ -duk_ret_t message(duk_context* ctx) +duk_ret_t Server_prototype_message(duk_context* ctx) { - self(ctx)->message(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); + return wrap(ctx, [] (auto ctx) { + auto target = dukx_require(ctx, 0); + auto message = dukx_get(ctx, 1); - return 0; + if (target.empty()) + throw server_error(server_error::invalid_nickname); + + self(ctx)->message(std::move(target), std::move(message)); + + return 0; + }); } +// }}} + +// {{{ Irccd.Server.prototype.mode + /* - * Method: Server.mode(channel, mode, limit, user, mask) + * Method: Irccd.Server.prototype.mode(channel, mode, limit, user, mask) * ------------------------------------------------------------------ * * Change your mode. * * Arguments: * - mode, the new mode. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. */ -duk_ret_t mode(duk_context* ctx) +duk_ret_t Server_prototype_mode(duk_context* ctx) { - self(ctx)->mode( - duk_require_string(ctx, 0), - duk_require_string(ctx, 1), - duk_opt_string(ctx, 2, ""), - duk_opt_string(ctx, 3, ""), - duk_opt_string(ctx, 4, "") - ); + return wrap(ctx, [] (auto ctx) { + auto channel = dukx_require(ctx, 0); + auto mode = dukx_require(ctx, 1); + auto limit = dukx_get(ctx, 2); + auto user = dukx_get(ctx, 3); + auto mask = dukx_get(ctx, 4); - return 0; + if (channel.empty()) + throw server_error(server_error::invalid_channel); + if (mode.empty()) + throw server_error(server_error::invalid_mode); + + self(ctx)->mode( + std::move(channel), + std::move(mode), + std::move(limit), + std::move(user), + std::move(mask) + ); + + return 0; + }); } +// }}} + +// {{{ Irccd.Server.prototype.names + /* - * Method: Server.names(channel) + * Method: Irccd.Server.prototype.names(channel) * ------------------------------------------------------------------ * * Get the list of names from a channel. * * Arguments: * - channel, the channel. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. */ -duk_ret_t names(duk_context* ctx) +duk_ret_t Server_prototype_names(duk_context* ctx) { - self(ctx)->names(duk_require_string(ctx, 0)); + return wrap(ctx, [] (auto ctx) { + auto channel = dukx_require(ctx, 0); - return 0; + if (channel.empty()) + throw server_error(server_error::invalid_channel); + + self(ctx)->names(std::move(channel)); + + return 0; + }); } +// }}} + +// {{{ Irccd.Server.prototype.nick + /* - * Method: Server.nick(nickname) + * Method: Irccd.Server.prototype.nick(nickname) * ------------------------------------------------------------------ * * Change the nickname. * * Arguments: * - nickname, the nickname. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. */ -duk_ret_t nick(duk_context* ctx) +duk_ret_t Server_prototype_nick(duk_context* ctx) { - self(ctx)->set_nickname(duk_require_string(ctx, 0)); + return wrap(ctx, [] (auto ctx) { + auto nickname = dukx_require(ctx, 0); - return 0; + if (nickname.empty()) + throw server_error(server_error::invalid_nickname); + + self(ctx)->set_nickname(std::move(nickname)); + + return 0; + }); } +// }}} + +// {{{ Irccd.Server.prototype.notice + /* - * Method: Server.notice(target, message) + * Method: Irccd.Server.prototype.notice(target, message) * ------------------------------------------------------------------ * * Send a private notice. @@ -252,16 +413,31 @@ * Arguments: * - target, the target, * - message, the notice message. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. */ -duk_ret_t notice(duk_context* ctx) +duk_ret_t Server_prototype_notice(duk_context* ctx) { - self(ctx)->notice(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); + return wrap(ctx, [] (auto ctx) { + auto target = dukx_require(ctx, 0); + auto message = dukx_get(ctx, 1); - return 0; + if (target.empty()) + throw server_error(server_error::invalid_nickname); + + self(ctx)->notice(std::move(target), std::move(message)); + + return 0; + }); } +// }}} + +// {{{ Irccd.Server.prototype.part + /* - * Method: Server.part(channel, reason = undefined) + * Method: Irccd.Server.prototype.part(channel, reason = undefined) * ------------------------------------------------------------------ * * Leave a channel. @@ -269,32 +445,61 @@ * Arguments: * - channel, the channel to leave, * - reason, the optional reason, keep undefined for portability. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. */ -duk_ret_t part(duk_context* ctx) +duk_ret_t Server_prototype_part(duk_context* ctx) { - self(ctx)->part(duk_require_string(ctx, 0), duk_get_string(ctx, 1)); + return wrap(ctx, [] (auto ctx) { + auto channel = dukx_require(ctx, 0); + auto reason = dukx_get(ctx, 1); - return 0; + if (channel.empty()) + throw server_error(server_error::invalid_channel); + + self(ctx)->part(std::move(channel), std::move(reason)); + + return 0; + }); } +// }}} + +// {{{ Irccd.Server.prototype.send + /* - * Method: Server.send(raw) + * Method: Irccd.Server.prototype.send(raw) * ------------------------------------------------------------------ * * Send a raw message to the IRC server. * * Arguments: * - raw, the raw message (without terminators). + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. */ -duk_ret_t send(duk_context* ctx) +duk_ret_t Server_prototype_send(duk_context* ctx) { - self(ctx)->send(duk_require_string(ctx, 0)); + return wrap(ctx, [] (auto ctx) { + auto raw = dukx_require(ctx, 0); - return 0; + if (raw.empty()) + throw server_error(server_error::invalid_message); + + self(ctx)->send(std::move(raw)); + + return 0; + }); } +// }}} + +// {{{ Irccd.Server.prototype.topic + /* - * Method: Server.topic(channel, topic) + * Method: Server.prototype.topic(channel, topic) * ------------------------------------------------------------------ * * Change a channel topic. @@ -302,47 +507,84 @@ * Arguments: * - channel, the channel, * - topic, the new topic. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. */ -duk_ret_t topic(duk_context* ctx) +duk_ret_t Server_prototype_topic(duk_context* ctx) { - self(ctx)->topic(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); + return wrap(ctx, [] (auto ctx) { + auto channel = dukx_require(ctx, 0); + auto topic = dukx_get(ctx, 1); - return 0; + if (channel.empty()) + throw server_error(server_error::invalid_channel); + + self(ctx)->topic(std::move(channel), std::move(topic)); + + return 0; + }); } +// }}} + +// {{{ Irccd.Server.prototype.whois + /* - * Method: Server.whois(target) + * Method: Irccd.Server.prototype.whois(target) * ------------------------------------------------------------------ * * Get whois information. * * Arguments: * - target, the target. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. */ -duk_ret_t whois(duk_context* ctx) +duk_ret_t Server_prototype_whois(duk_context* ctx) { - self(ctx)->whois(duk_require_string(ctx, 0)); + return wrap(ctx, [] (auto ctx) { + auto target = dukx_require(ctx, 0); - return 0; + if (target.empty()) + throw server_error(server_error::invalid_nickname); + + self(ctx)->whois(std::move(target)); + + return 0; + }); } +// }}} + +// {{{ Irccd.Server.prototype.toString + /* - * Method: Server.toString() + * Method: Irccd.Server.prototype.toString() * ------------------------------------------------------------------ * * Convert the object to std::string, convenience for adding the object * as property key. * - * duk_ret_turns: + * Returns: * The server name (unique). + * Throws: + * - Irccd.SystemError on errors. */ -duk_ret_t toString(duk_context* ctx) +duk_ret_t Server_prototype_toString(duk_context* ctx) { - dukx_push(ctx, self(ctx)->get_name()); + return wrap(ctx, [] (auto ctx) { + dukx_push(ctx, self(ctx)->get_name()); - return 1; + return 1; + }); } +// }}} + +// {{{ Irccd.Server [constructor] + /* * Function: Irccd.Server(params) [constructor] * ------------------------------------------------------------------ @@ -363,15 +605,21 @@ * username: "user name", (Optional, default: irccd) * realname: "real name", (Optional, default: IRC Client Daemon) * commandChar: "!", (Optional, the command char, default: "!") + * + * Arguments: + * - params, the server properties + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. */ -duk_ret_t constructor(duk_context* ctx) +duk_ret_t Server_constructor(duk_context* ctx) { - if (!duk_is_constructor_call(ctx)) - return 0; + return wrap(ctx, [] (auto ctx) { + if (!duk_is_constructor_call(ctx)) + return 0; - duk_check_type(ctx, 0, DUK_TYPE_OBJECT); + duk_check_type(ctx, 0, DUK_TYPE_OBJECT); - try { auto json = nlohmann::json::parse(duk_json_encode(ctx, 0)); auto s = server_util::from_json(dukx_type_traits::self(ctx).get_service(), json); @@ -379,20 +627,22 @@ duk_push_pointer(ctx, new std::shared_ptr(std::move(s))); duk_put_prop_string(ctx, -2, signature); duk_pop(ctx); - } catch (const std::exception& ex) { - duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what()); - } - return 0; + return 0; + }); } +// }}} + +// {{{ Irccd.Server [destructor] + /* * Function: Irccd.Server() [destructor] * ------------------------------------------------------------------ * * Delete the property. */ -duk_ret_t destructor(duk_context* ctx) +duk_ret_t Server_destructor(duk_context* ctx) { duk_get_prop_string(ctx, 0, signature); delete static_cast*>(duk_to_pointer(ctx, -1)); @@ -402,6 +652,10 @@ return 0; } +// }}} + +// {{{ Irccd.Server.add + /* * Function: Irccd.Server.add(s) * ------------------------------------------------------------------ @@ -410,14 +664,23 @@ * * Arguments: * - s, the server to add. + * Throws: + * - Irccd.SystemError on errors. */ -duk_ret_t add(duk_context* ctx) +duk_ret_t Server_add(duk_context* ctx) { - dukx_type_traits::self(ctx).servers().add(dukx_require>(ctx, 0)); + return wrap(ctx, [] (auto ctx) { + dukx_type_traits::self(ctx).servers().add( + dukx_require>(ctx, 0)); - return 0; + return 0; + }); } +// }}} + +// {{{ Irccd.Server.find + /* * Function: Irccd.Server.find(name) * ------------------------------------------------------------------ @@ -426,20 +689,29 @@ * * Arguments: * - name, the server name - * duk_ret_turns: + * Returns: * The server object or undefined if not found. + * Throws: + * - Irccd.SystemError on errors. */ -duk_ret_t find(duk_context* ctx) +duk_ret_t Server_find(duk_context* ctx) { - auto server = dukx_type_traits::self(ctx).servers().get(duk_require_string(ctx, 0)); + return wrap(ctx, [] (auto ctx) { + auto id = dukx_require(ctx, 0); + auto server = dukx_type_traits::self(ctx).servers().get(id); - if (!server) - return 0; + if (!server) + return 0; + + dukx_push(ctx, server); - dukx_push(ctx, server); + return 1; + }); +} - return 1; -} +// }}} + +// {{{ Irccd.Server.list /* * Function: Irccd.Server.list() @@ -447,14 +719,14 @@ * * Get the map of all loaded servers. * - * duk_ret_turns: + * Returns: * An object with string-to-servers pairs. */ -duk_ret_t list(duk_context* ctx) +duk_ret_t Server_list(duk_context* ctx) { duk_push_object(ctx); - for (const auto &server : dukx_type_traits::self(ctx).servers().servers()) { + for (const auto& server : dukx_type_traits::self(ctx).servers().servers()) { dukx_push(ctx, server); duk_put_prop_string(ctx, -2, server->get_name().c_str()); } @@ -462,8 +734,12 @@ return 1; } +// }}} + +// {{{ Irccd.Server.remove + /* - * Function: irccd.Server.remove(name) + * Function: Irccd.Server.remove(name) * ------------------------------------------------------------------ * * Remove a server from the irccd instance. You can pass the server object since @@ -472,39 +748,69 @@ * Arguments: * - name the server name. */ -duk_ret_t remove(duk_context* ctx) +duk_ret_t Server_remove(duk_context* ctx) { dukx_type_traits::self(ctx).servers().remove(duk_require_string(ctx, 0)); return 0; } +// }}} + +// {{{ Irccd.ServerError + +/* + * Function: Irccd.ServerError(code, message) + * ------------------------------------------------------------------ + * + * Create an Irccd.ServerError object. + * + * Arguments: + * - code, the error code, + * - message, the error message. + */ +duk_ret_t ServerError_constructor(duk_context* ctx) +{ + duk_push_this(ctx); + duk_push_int(ctx, duk_require_int(ctx, 0)); + duk_put_prop_string(ctx, -2, "code"); + duk_push_string(ctx, duk_require_string(ctx, 1)); + duk_put_prop_string(ctx, -2, "message"); + duk_push_string(ctx, "ServerError"); + duk_put_prop_string(ctx, -2, "name"); + duk_pop(ctx); + + return 0; +} + +// }}} + const duk_function_list_entry methods[] = { - { "info", info, 0 }, - { "invite", invite, 2 }, - { "isSelf", isSelf, 1 }, - { "join", join, DUK_VARARGS }, - { "kick", kick, DUK_VARARGS }, - { "me", me, 2 }, - { "message", message, 2 }, - { "mode", mode, 1 }, - { "names", names, 1 }, - { "nick", nick, 1 }, - { "notice", notice, 2 }, - { "part", part, DUK_VARARGS }, - { "send", send, 1 }, - { "topic", topic, 2 }, - { "whois", whois, 1 }, - { "toString", toString, 0 }, - { nullptr, nullptr, 0 } + { "info", Server_prototype_info, 0 }, + { "invite", Server_prototype_invite, 2 }, + { "isSelf", Server_prototype_isSelf, 1 }, + { "join", Server_prototype_join, DUK_VARARGS }, + { "kick", Server_prototype_kick, DUK_VARARGS }, + { "me", Server_prototype_me, 2 }, + { "message", Server_prototype_message, 2 }, + { "mode", Server_prototype_mode, 1 }, + { "names", Server_prototype_names, 1 }, + { "nick", Server_prototype_nick, 1 }, + { "notice", Server_prototype_notice, 2 }, + { "part", Server_prototype_part, DUK_VARARGS }, + { "send", Server_prototype_send, 1 }, + { "topic", Server_prototype_topic, 2 }, + { "toString", Server_prototype_toString, 0 }, + { "whois", Server_prototype_whois, 1 }, + { nullptr, nullptr, 0 } }; const duk_function_list_entry functions[] = { - { "add", add, 1 }, - { "find", find, 1 }, - { "list", list, 0 }, - { "remove", remove, 1 }, - { nullptr, nullptr, 0 } + { "add", Server_add, 1 }, + { "find", Server_find, 1 }, + { "list", Server_list, 0 }, + { "remove", Server_remove, 1 }, + { nullptr, nullptr, 0 } }; } // !namespace @@ -519,11 +825,23 @@ dukx_stack_assert sa(plugin->context()); duk_get_global_string(plugin->context(), "Irccd"); - duk_push_c_function(plugin->context(), constructor, 1); + + // ServerError function. + duk_push_c_function(plugin->context(), ServerError_constructor, 2); + duk_push_object(plugin->context()); + duk_get_global_string(plugin->context(), "Error"); + duk_get_prop_string(plugin->context(), -1, "prototype"); + duk_remove(plugin->context(), -2); + duk_set_prototype(plugin->context(), -2); + duk_put_prop_string(plugin->context(), -2, "prototype"); + duk_put_prop_string(plugin->context(), -2, "ServerError"); + + // Server constructor. + duk_push_c_function(plugin->context(), Server_constructor, 1); duk_put_function_list(plugin->context(), -1, functions); duk_push_object(plugin->context()); duk_put_function_list(plugin->context(), -1, methods); - duk_push_c_function(plugin->context(), destructor, 1); + duk_push_c_function(plugin->context(), Server_destructor, 1); duk_set_finalizer(plugin->context(), -2); duk_dup_top(plugin->context()); duk_put_global_string(plugin->context(), prototype); @@ -533,6 +851,7 @@ } using server_traits = dukx_type_traits>; +using server_error_traits = dukx_type_traits; void server_traits::push(duk_context* ctx, std::shared_ptr server) { @@ -560,4 +879,18 @@ return file; } +void server_error_traits::raise(duk_context* ctx, const server_error& ex) +{ + dukx_stack_assert sa(ctx, 1); + + duk_get_global_string(ctx, "Irccd"); + duk_get_prop_string(ctx, -1, "ServerError"); + duk_remove(ctx, -2); + dukx_push(ctx, ex.code().value()); + dukx_push(ctx, ex.code().message()); + duk_new(ctx, 2); + + (void)duk_throw(ctx); +} + } // !irccd diff -r e81c6badede3 -r a059100b73dc libirccd-js/irccd/js/server_jsapi.hpp --- a/libirccd-js/irccd/js/server_jsapi.hpp Fri Apr 13 22:43:10 2018 +0200 +++ b/libirccd-js/irccd/js/server_jsapi.hpp Fri Apr 13 23:05:56 2018 +0200 @@ -74,6 +74,21 @@ static std::shared_ptr require(duk_context* ctx, duk_idx_t index); }; +/** + * \brief Specialization for server_error. + */ +template <> +class dukx_type_traits : public std::true_type { +public: + /** + * Raise a server_error. + * + * \param ctx the context + * \param error the error + */ + static void raise(duk_context* ctx, const server_error& error); +}; + } // !irccd #endif // !IRCCD_JS_SERVER_JSAPI_HPP diff -r e81c6badede3 -r a059100b73dc libirccd-js/irccd/js/system_jsapi.cpp --- a/libirccd-js/irccd/js/system_jsapi.cpp Fri Apr 13 22:43:10 2018 +0200 +++ b/libirccd-js/irccd/js/system_jsapi.cpp Fri Apr 13 23:05:56 2018 +0200 @@ -37,6 +37,26 @@ namespace { +// {{{ wrap + +template +duk_ret_t wrap(duk_context* ctx, Handler handler) +{ + try { + return handler(); + } catch (const std::system_error& ex) { + dukx_throw(ctx, ex); + } catch (const std::exception& ex) { + dukx_throw(ctx, ex); + } + + return 0; +} + +// }}} + +// {{{ Irccd.System.env + /* * Function: Irccd.System.env(key) * ------------------------------------------------------------------ @@ -47,12 +67,20 @@ * - key, the environment variable. * Returns: * The value. + * Throws: + * - Irccd.SystemError on errors. */ -duk_ret_t env(duk_context* ctx) +duk_ret_t System_env(duk_context* ctx) { - return dukx_push(ctx, sys::env(duk_get_string(ctx, 0))); + return wrap(ctx, [&] { + return dukx_push(ctx, sys::env(dukx_get(ctx, 0))); + }); } +// }}} + +// {{{ Irccd.System.exec + /* * Function: Irccd.System.exec(cmd) * ------------------------------------------------------------------ @@ -61,14 +89,20 @@ * * Arguments: * - cmd, the command to execute. + * Throws: + * - Irccd.SystemError on errors. */ -duk_ret_t exec(duk_context* ctx) +duk_ret_t System_exec(duk_context* ctx) { std::system(duk_require_string(ctx, 0)); return 0; } +// }}} + +// {{{ Irccd.System.home + /* * Function: Irccd.System.home() * ------------------------------------------------------------------ @@ -77,12 +111,20 @@ * * Returns: * The user home directory. + * Throws: + * - Irccd.SystemError on errors. */ -duk_ret_t home(duk_context* ctx) +duk_ret_t System_home(duk_context* ctx) { - return dukx_push(ctx, sys::home()); + return wrap(ctx, [&] { + return dukx_push(ctx, sys::home()); + }); } +// }}} + +// {{{ Irccd.System.name + /* * Function: Irccd.System.name() * ------------------------------------------------------------------ @@ -91,12 +133,20 @@ * * Returns: * The system name. + * Throws: + * - Irccd.SystemError on errors. */ -duk_ret_t name(duk_context* ctx) +duk_ret_t System_name(duk_context* ctx) { - return dukx_push(ctx, sys::name()); + return wrap(ctx, [&] { + return dukx_push(ctx, sys::name()); + }); } +// }}} + +// {{{ Irccd.System.popen + #if defined(HAVE_POPEN) /* @@ -110,34 +160,51 @@ * - mode, the mode (e.g. "r"). * Returns: * A irccd.File object. - * Throws - * - irccd.system_error on failures. + * Throws: + * - Irccd.SystemError on errors. */ -duk_ret_t popen(duk_context* ctx) +duk_ret_t System_popen(duk_context* ctx) { - auto fp = ::popen(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); + return wrap(ctx, [&] { + auto fp = ::popen(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); - if (fp == nullptr) - dukx_throw(ctx, system_error()); + if (fp == nullptr) + throw std::system_error(make_error_code(static_cast(errno))); - return dukx_push(ctx, std::make_shared(fp, [] (auto fp) { ::pclose(fp); })); + return dukx_push(ctx, std::make_shared(fp, [] (auto fp) { ::pclose(fp); })); + }); } #endif // !HAVE_POPEN +// }}} + +// {{{ Icrcd.System.sleep + /* * Function: Irccd.System.sleep(delay) * ------------------------------------------------------------------ * * Sleep the main loop for the specific delay in seconds. + * + * Arguments: + * - delay, the delay in seconds. + * Throws: + * - Irccd.SystemError on errors. */ -duk_ret_t sleep(duk_context* ctx) +duk_ret_t System_sleep(duk_context* ctx) { - std::this_thread::sleep_for(std::chrono::seconds(duk_get_int(ctx, 0))); + return wrap(ctx, [&] { + std::this_thread::sleep_for(std::chrono::seconds(duk_get_int(ctx, 0))); - return 0; + return 0; + }); } +// }}} + +// {{{ Irccd.System.ticks + /* * Function: Irccd.System.ticks() * ------------------------------------------------------------------ @@ -146,73 +213,100 @@ * * Returns: * The number of milliseconds. + * Throws: + * - Irccd.SystemError on errors. */ -duk_ret_t ticks(duk_context* ctx) +duk_ret_t System_ticks(duk_context* ctx) { - duk_push_int(ctx, sys::ticks()); - - return 1; + return wrap(ctx, [&] { + return dukx_push(ctx, sys::ticks()); + }); } +// }}} + +// {{{ Irccd.System.usleep + /* - * Function: irccd.System.usleep(delay) + * Function: Irccd.System.usleep(delay) * ------------------------------------------------------------------ * * Sleep the main loop for the specific delay in microseconds. + * + * Arguments: + * - delay, the delay in microseconds. + * Throws: + * - Irccd.SystemError on errors. */ -duk_ret_t usleep(duk_context* ctx) +duk_ret_t System_usleep(duk_context* ctx) { - std::this_thread::sleep_for(std::chrono::microseconds(duk_get_int(ctx, 0))); + return wrap(ctx, [&] { + std::this_thread::sleep_for(std::chrono::microseconds(duk_get_int(ctx, 0))); - return 0; + return 0; + }); } +// }}} + +// {{{ Irccd.System.uptime + /* - * Function: irccd.System.uptime() + * Function: Irccd.System.uptime() * ------------------------------------------------------------------ * * Get the system uptime. * * Returns: * The system uptime. + * Throws: + * - Irccd.SystemError on errors. */ -duk_ret_t uptime(duk_context* ctx) +duk_ret_t System_uptime(duk_context* ctx) { - duk_push_int(ctx, sys::uptime()); - - return 0; + return wrap(ctx, [&] { + return dukx_push(ctx, sys::uptime()); + }); } +// }}} + +// {{{ Irccd.System.version + /* - * Function: irccd.System.version() + * Function: Irccd.System.version() * ------------------------------------------------------------------ * * Get the operating system version. * * Returns: * The system version. + * Throws: + * - Irccd.SystemError on errors. */ -duk_ret_t version(duk_context* ctx) +duk_ret_t System_version(duk_context* ctx) { - dukx_push(ctx, sys::version()); + return wrap(ctx, [&] { + return dukx_push(ctx, sys::version()); + }); +} - return 1; -} +// }}} const duk_function_list_entry functions[] = { - { "env", env, 1 }, - { "exec", exec, 1 }, - { "home", home, 0 }, - { "name", name, 0 }, + { "env", System_env, 1 }, + { "exec", System_exec, 1 }, + { "home", System_home, 0 }, + { "name", System_name, 0 }, #if defined(HAVE_POPEN) - { "popen", popen, 2 }, + { "popen", System_popen, 2 }, #endif - { "sleep", sleep, 1 }, - { "ticks", ticks, 0 }, - { "uptime", uptime, 0 }, - { "usleep", usleep, 1 }, - { "version", version, 0 }, - { nullptr, nullptr, 0 } + { "sleep", System_sleep, 1 }, + { "ticks", System_ticks, 0 }, + { "uptime", System_uptime, 0 }, + { "usleep", System_usleep, 1 }, + { "version", System_version, 0 }, + { nullptr, nullptr, 0 } }; } // !namespace diff -r e81c6badede3 -r a059100b73dc libirccd-js/irccd/js/timer_jsapi.cpp --- a/libirccd-js/irccd/js/timer_jsapi.cpp Fri Apr 13 22:43:10 2018 +0200 +++ b/libirccd-js/irccd/js/timer_jsapi.cpp Fri Apr 13 23:05:56 2018 +0200 @@ -33,6 +33,8 @@ const char* signature("\xff""\xff""irccd-timer-ptr"); const char* table("\xff""\xff""irccd-timer-callbacks"); +// {{{ timer + class timer { public: enum class type_t { @@ -122,6 +124,10 @@ } } +// }}} + +// {{{ self + timer* self(duk_context* ctx) { dukx_stack_assert sa(ctx); @@ -137,37 +143,43 @@ return static_cast(ptr); } +// }}} + +// {{{ Irccd.Timer.prototype.start + /* - * Method: timer.start() + * Method: Irccd.Timer.prototype.start() * -------------------------------------------------------- * * Start the timer. If the timer is already started the method is a no-op. */ -duk_ret_t start(duk_context* ctx) +duk_ret_t Timer_prototype_start(duk_context* ctx) { self(ctx)->start(); return 0; } +// }}} + +// {{{ Irccd.Timer.prototype.stop + /* - * Method: timer.stop() + * Method: Irccd.Timer.prototype.stop() * -------------------------------------------------------- * * Stop the timer. */ -duk_ret_t stop(duk_context* ctx) +duk_ret_t Timer_prototype_stop(duk_context* ctx) { self(ctx)->stop(); return 0; } -const duk_function_list_entry methods[] = { - { "start", start, 0 }, - { "stop", stop, 0 }, - { nullptr, nullptr, 0 } -}; +// }}} + +// {{{ Irccd.Timer [destructor] /* * Function: Irccd.Timer() [destructor] @@ -175,7 +187,7 @@ * * Deleter the timer. */ -duk_ret_t destructor(duk_context* ctx) +duk_ret_t Timer_destructor(duk_context* ctx) { dukx_stack_assert sa(ctx); @@ -196,8 +208,12 @@ return 0; } +// }}} + +// {{{ Irccd.Timer [constructor] + /* - * Function: Irccd.timer(type, delay, callback) [constructor] + * Function: Irccd.Timer(type, delay, callback) [constructor] * -------------------------------------------------------- * * Create a new timer object. @@ -207,41 +223,53 @@ * - delay, the interval in milliseconds, * - callback, the function to call. */ -duk_ret_t constructor(duk_context* ctx) +duk_ret_t Timer_constructor(duk_context* ctx) { if (!duk_is_constructor_call(ctx)) return 0; - // Check parameters. - auto type = duk_require_int(ctx, 0); - auto delay = duk_require_int(ctx, 1); + try { + // Check parameters. + const auto type = duk_require_int(ctx, 0); + const auto delay = duk_require_int(ctx, 1); - if (type < static_cast(timer::type_t::single) || type > static_cast(timer::type_t::repeat)) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid timer type"); - if (delay < 0) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "negative delay given"); - if (!duk_is_callable(ctx, 2)) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "missing callback function"); + if (type < static_cast(timer::type_t::single) || type > static_cast(timer::type_t::repeat)) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid timer type"); + if (delay < 0) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "negative delay given"); + if (!duk_is_callable(ctx, 2)) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "missing callback function"); - auto plugin = dukx_type_traits::self(ctx); - auto& daemon = dukx_type_traits::self(ctx); - auto object = new timer(daemon.get_service(), plugin, delay, static_cast(type)); + auto plugin = dukx_type_traits::self(ctx); + auto& daemon = dukx_type_traits::self(ctx); + auto object = new timer(daemon.get_service(), plugin, delay, static_cast(type)); - duk_push_this(ctx); - duk_push_pointer(ctx, object); - duk_put_prop_string(ctx, -2, signature); - duk_push_c_function(ctx, destructor, 1); - duk_set_finalizer(ctx, -2); - duk_pop(ctx); + duk_push_this(ctx); + duk_push_pointer(ctx, object); + duk_put_prop_string(ctx, -2, signature); + duk_push_c_function(ctx, Timer_destructor, 1); + duk_set_finalizer(ctx, -2); + duk_pop(ctx); - // Store the function in a table to be called later. - duk_get_global_string(ctx, table); - duk_dup(ctx, 2); - duk_put_prop_string(ctx, -2, object->key().c_str()); + // Store the function in a table to be called later. + duk_get_global_string(ctx, table); + duk_dup(ctx, 2); + duk_put_prop_string(ctx, -2, object->key().c_str()); + } catch (const std::exception& ex) { + dukx_throw(ctx, ex); + } return 0; } +// }}} + +const duk_function_list_entry methods[] = { + { "start", Timer_prototype_start, 0 }, + { "stop", Timer_prototype_stop, 0 }, + { nullptr, nullptr, 0 } +}; + const duk_number_list_entry constants[] = { { "Single", static_cast(timer::type_t::single) }, { "Repeat", static_cast(timer::type_t::repeat) }, @@ -260,7 +288,7 @@ dukx_stack_assert sa(plugin->context()); duk_get_global_string(plugin->context(), "Irccd"); - duk_push_c_function(plugin->context(), constructor, 3); + duk_push_c_function(plugin->context(), Timer_constructor, 3); duk_put_number_list(plugin->context(), -1, constants); duk_push_object(plugin->context()); duk_put_function_list(plugin->context(), -1, methods); diff -r e81c6badede3 -r a059100b73dc libirccd-js/irccd/js/unicode_jsapi.cpp --- a/libirccd-js/irccd/js/unicode_jsapi.cpp Fri Apr 13 22:43:10 2018 +0200 +++ b/libirccd-js/irccd/js/unicode_jsapi.cpp Fri Apr 13 23:05:56 2018 +0200 @@ -24,6 +24,8 @@ namespace { +// {{{ Irccd.Unicode.isDigit + /* * Function: Irccd.Unicode.isDigit(code) * -------------------------------------------------------- @@ -33,12 +35,14 @@ * Returns: * True if the code is in the digit category. */ -duk_ret_t is_digit(duk_context* ctx) +duk_ret_t Unicode_isDigit(duk_context* ctx) noexcept { - duk_push_boolean(ctx, unicode::isdigit(duk_get_int(ctx, 0))); + return dukx_push(ctx, unicode::isdigit(duk_get_int(ctx, 0))); +} - return 1; -} +// }}} + +// {{{ Irccd.Unicode.isLetter /* * Function: Irccd.Unicode.isLetter(code) @@ -49,12 +53,14 @@ * Returns: * True if the code is in the letter category. */ -duk_ret_t is_letter(duk_context* ctx) +duk_ret_t Unicode_isLetter(duk_context* ctx) noexcept { - duk_push_boolean(ctx, unicode::isalpha(duk_get_int(ctx, 0))); + return dukx_push(ctx, unicode::isalpha(duk_get_int(ctx, 0))); +} - return 1; -} +// }}} + +// {{{ Irccd.Unicode.isLower /* * Function: Irccd.Unicode.isLower(code) @@ -65,12 +71,14 @@ * Returns: * True if the code is lower case. */ -duk_ret_t is_lower(duk_context* ctx) +duk_ret_t Unicode_isLower(duk_context* ctx) noexcept { - duk_push_boolean(ctx, unicode::islower(duk_get_int(ctx, 0))); + return dukx_push(ctx, unicode::islower(duk_get_int(ctx, 0))); +} - return 1; -} +// }}} + +// {{{ Irccd.Unicode.isSpace /* * Function: Irccd.Unicode.isSpace(code) @@ -81,12 +89,14 @@ * Returns: * True if the code is in the space category. */ -duk_ret_t is_space(duk_context* ctx) +duk_ret_t Unicode_isSpace(duk_context* ctx) noexcept { - duk_push_boolean(ctx, unicode::isspace(duk_get_int(ctx, 0))); + return dukx_push(ctx, unicode::isspace(duk_get_int(ctx, 0))); +} - return 1; -} +// }}} + +// {{{ Irccd.Unicode.isTitle /* * Function: Irccd.Unicode.isTitle(code) @@ -97,12 +107,14 @@ * Returns: * True if the code is title case. */ -duk_ret_t is_title(duk_context* ctx) +duk_ret_t Unicode_isTitle(duk_context* ctx) noexcept { - duk_push_boolean(ctx, unicode::istitle(duk_get_int(ctx, 0))); + return dukx_push(ctx, unicode::istitle(duk_get_int(ctx, 0))); +} - return 1; -} +// }}} + +// {{{ Irccd.Unicode.isUpper /* * Function: Irccd.Unicode.isUpper(code) @@ -113,21 +125,21 @@ * Returns: * True if the code is upper case. */ -duk_ret_t is_upper(duk_context* ctx) +duk_ret_t Unicode_isUpper(duk_context* ctx) noexcept { - duk_push_boolean(ctx, unicode::isupper(duk_get_int(ctx, 0))); + return dukx_push(ctx, unicode::isupper(duk_get_int(ctx, 0))); +} - return 1; -} +// }}} const duk_function_list_entry functions[] = { - { "isDigit", is_digit, 1 }, - { "isLetter", is_letter, 1 }, - { "isLower", is_lower, 1 }, - { "isSpace", is_space, 1 }, - { "isTitle", is_title, 1 }, - { "isUpper", is_upper, 1 }, - { nullptr, nullptr, 0 } + { "isDigit", Unicode_isDigit, 1 }, + { "isLetter", Unicode_isLetter, 1 }, + { "isLower", Unicode_isLower, 1 }, + { "isSpace", Unicode_isSpace, 1 }, + { "isTitle", Unicode_isTitle, 1 }, + { "isUpper", Unicode_isUpper, 1 }, + { nullptr, nullptr, 0 } }; } // !namespace diff -r e81c6badede3 -r a059100b73dc libirccd-js/irccd/js/util_jsapi.cpp --- a/libirccd-js/irccd/js/util_jsapi.cpp Fri Apr 13 22:43:10 2018 +0200 +++ b/libirccd-js/irccd/js/util_jsapi.cpp Fri Apr 13 23:05:56 2018 +0200 @@ -21,6 +21,7 @@ #include #include "duktape_vector.hpp" +#include "irccd_jsapi.hpp" #include "js_plugin.hpp" #include "util_jsapi.hpp" @@ -28,8 +29,10 @@ namespace { +// {{{ subst + /* - * Read parameters for irccd.Util.format function, the object is defined as + * Read parameters for Irccd.Util.format function, the object is defined as * following: * * { @@ -40,7 +43,7 @@ * fieldn: ... * } */ -string_util::subst get_subst(duk_context* ctx, int index) +string_util::subst subst(duk_context* ctx, int index) { string_util::subst params; @@ -64,6 +67,10 @@ return params; } +// }}} + +// {{{ split + /* * split (for Irccd.Util.cut) * ------------------------------------------------------------------ @@ -84,7 +91,7 @@ while (duk_next(ctx, -1, 1)) { // Split individual tokens as array if spaces are found. - auto tmp = string_util::split(duk_to_string(ctx, -1), pattern); + const auto tmp = string_util::split(duk_to_string(ctx, -1), pattern); result.insert(result.end(), tmp.begin(), tmp.end()); duk_pop_2(ctx); @@ -94,6 +101,10 @@ return result; } +// }}} + +// {{{ limit + /* * limit (for Irccd.Util.cut) * ------------------------------------------------------------------ @@ -115,6 +126,10 @@ return value; } +// }}} + +// {{{ lines + /* * lines (for Irccd.Util.cut) * ------------------------------------------------------------------ @@ -159,6 +174,28 @@ return result; } +// }}} + +// {{{ wrap + +template +duk_ret_t wrap(duk_context* ctx, Handler handler) +{ + try { + return handler(); + } catch (const std::system_error& ex) { + dukx_throw(ctx, ex); + } catch (const std::exception& ex) { + dukx_throw(ctx, ex); + } + + return 0; +} + +// }}} + +// {{{ Irccd.Util.cut + /* * Function: Irccd.Util.cut(data, maxc, maxl) * -------------------------------------------------------- @@ -189,24 +226,31 @@ * Throws: * - RangeError if maxl or maxc are negative numbers, * - RangeError if one word length was bigger than maxc, - * - TypeError if data is not a string or a list of strings. + * - TypeError if data is not a string or a list of strings, + * - Irccd.SystemError on other errors. */ -duk_ret_t cut(duk_context* ctx) +duk_ret_t Util_cut(duk_context* ctx) { - auto list = lines(ctx, split(ctx), limit(ctx, 1, "maxc", 72)); - auto maxl = limit(ctx, 2, "maxl", INT_MAX); + return wrap(ctx, [&] { + const auto list = lines(ctx, split(ctx), limit(ctx, 1, "maxc", 72)); + const auto maxl = limit(ctx, 2, "maxl", INT_MAX); + + if (list.size() > static_cast(maxl)) + return 0; - if (list.size() > static_cast(maxl)) - return 0; + // Empty list but lines() returns at least one. + if (list.size() == 1 && list[0].empty()) { + duk_push_array(ctx); + return 1; + } - // Empty list but lines() returns at least one. - if (list.size() == 1 && list[0].empty()) { - duk_push_array(ctx); - return 1; - } + return dukx_push(ctx, list); + }); +} - return dukx_push(ctx, list); -} +// }}} + +// {{{ Irccd.Util.format /* * Function: Irccd.Util.format(text, parameters) @@ -219,17 +263,19 @@ * - params, the parameters. * Returns: * The converted text. + * Throws: + * - Irccd.SystemError on errors. */ -duk_ret_t format(duk_context* ctx) +duk_ret_t Util_format(duk_context* ctx) { - try { - dukx_push(ctx, string_util::format(duk_get_string(ctx, 0), get_subst(ctx, 1))); - } catch (const std::exception &ex) { - (void)duk_error(ctx, DUK_ERR_SYNTAX_ERROR, "%s", ex.what()); - } + return wrap(ctx, [&] { + return dukx_push(ctx, string_util::format(dukx_get(ctx, 0), subst(ctx, 1))); + }); +} - return 1; -} +// }}} + +// {{{ Irccd.Util.splituser /* * Function: Irccd.Util.splituser(ident) @@ -241,13 +287,19 @@ * - ident, the full identity. * Returns: * The nickname. + * Throws: + * - Irccd.SystemError on errors. */ -duk_ret_t splituser(duk_context* ctx) +duk_ret_t Util_splituser(duk_context* ctx) { - dukx_push(ctx, irc::user::parse(duk_require_string(ctx, 0)).nick()); + return wrap(ctx, [&] { + return dukx_push(ctx, irc::user::parse(dukx_require(ctx, 0)).nick()); + }); +} - return 1; -} +// }}} + +// {{{ Irccd.Util.splithost /* * Function: Irccd.Util.splithost(ident) @@ -259,20 +311,24 @@ * - ident, the full identity. * Returns: * The hostname. + * Throws: + * - Irccd.SystemError on errors. */ -duk_ret_t splithost(duk_context* ctx) +duk_ret_t Util_splithost(duk_context* ctx) { - dukx_push(ctx, irc::user::parse(duk_require_string(ctx, 0)).host()); - - return 1; + return wrap(ctx, [&] { + return dukx_push(ctx, irc::user::parse(dukx_require(ctx, 0)).host()); + }); } +// }}} + const duk_function_list_entry functions[] = { - { "cut", cut, DUK_VARARGS }, - { "format", format, DUK_VARARGS }, - { "splituser", splituser, 1 }, - { "splithost", splithost, 1 }, - { nullptr, nullptr, 0 } + { "cut", Util_cut, DUK_VARARGS }, + { "format", Util_format, DUK_VARARGS }, + { "splituser", Util_splituser, 1 }, + { "splithost", Util_splithost, 1 }, + { nullptr, nullptr, 0 } }; } // !namespace diff -r e81c6badede3 -r a059100b73dc libirccd/irccd/daemon/server.cpp --- a/libirccd/irccd/daemon/server.cpp Fri Apr 13 22:43:10 2018 +0200 +++ b/libirccd/irccd/daemon/server.cpp Fri Apr 13 23:05:56 2018 +0200 @@ -555,6 +555,8 @@ void server::join(std::string channel, std::string password) { + assert(!channel.empty()); + auto it = std::find_if(rchannels_.begin(), rchannels_.end(), [&] (const auto& c) { return c.name == channel; }); diff -r e81c6badede3 -r a059100b73dc tests/src/libirccd-js/jsapi-irccd/main.cpp --- a/tests/src/libirccd-js/jsapi-irccd/main.cpp Fri Apr 13 22:43:10 2018 +0200 +++ b/tests/src/libirccd-js/jsapi-irccd/main.cpp Fri Apr 13 23:05:56 2018 +0200 @@ -76,7 +76,7 @@ BOOST_AUTO_TEST_CASE(from_native) { duk_push_c_function(plugin_->context(), [] (duk_context *ctx) -> duk_ret_t { - dukx_throw(ctx, system_error(EINVAL, "hey")); + dukx_throw(ctx, std::system_error(make_error_code(std::errc::invalid_argument))); return 0; }, 0); @@ -89,7 +89,6 @@ "} catch (e) {" " errno = e.errno;" " name = e.name;" - " message = e.message;" " v1 = (e instanceof Error);" " v2 = (e instanceof Irccd.SystemError);" "}" @@ -102,8 +101,6 @@ BOOST_TEST(EINVAL == duk_get_int(plugin_->context(), -1)); BOOST_TEST(duk_get_global_string(plugin_->context(), "name")); BOOST_TEST("SystemError" == duk_get_string(plugin_->context(), -1)); - BOOST_TEST(duk_get_global_string(plugin_->context(), "message")); - BOOST_TEST("hey" == duk_get_string(plugin_->context(), -1)); BOOST_TEST(duk_get_global_string(plugin_->context(), "v1")); BOOST_TEST(duk_get_boolean(plugin_->context(), -1)); BOOST_TEST(duk_get_global_string(plugin_->context(), "v2"));