changeset 565:194162717ec9

Irccd: bring new duktape.hpp
author David Demelier <markand@malikania.fr>
date Mon, 27 Nov 2017 10:16:45 +0100
parents b8ebbc74da0c
children bf56628e070b
files libirccd-js/irccd/js/directory_jsapi.cpp libirccd-js/irccd/js/duktape.hpp libirccd-js/irccd/js/elapsed_timer_jsapi.cpp libirccd-js/irccd/js/file_jsapi.cpp libirccd-js/irccd/js/irccd_jsapi.cpp libirccd-js/irccd/js/js_plugin.cpp libirccd-js/irccd/js/js_plugin.hpp libirccd-js/irccd/js/logger_jsapi.cpp libirccd-js/irccd/js/plugin_jsapi.cpp libirccd-js/irccd/js/server_jsapi.cpp libirccd-js/irccd/js/system_jsapi.cpp libirccd-js/irccd/js/timer_jsapi.cpp libirccd-js/irccd/js/unicode_jsapi.cpp libirccd-js/irccd/js/util_jsapi.cpp tests/CMakeLists.txt tests/js-directory/main.cpp tests/js-elapsedtimer/main.cpp tests/js-file/main.cpp tests/js-irccd/main.cpp tests/js-logger/main.cpp tests/js-system/main.cpp tests/js-util/main.cpp tests/js/CMakeLists.txt tests/js/main.cpp tests/js/syntax-error.js
diffstat 25 files changed, 343 insertions(+), 634 deletions(-) [+]
line wrap: on
line diff
--- a/libirccd-js/irccd/js/directory_jsapi.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/libirccd-js/irccd/js/directory_jsapi.cpp	Mon Nov 27 10:16:45 2017 +0100
@@ -46,7 +46,7 @@
     if (duk_get_type(ctx, -1) != DUK_TYPE_STRING)
         duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Directory object");
 
-    auto ret = dukx_get_std_string(ctx, -1);
+    auto ret = dukx_get_string(ctx, -1);
 
     if (ret.empty())
         duk_error(ctx, DUK_ERR_TYPE_ERROR, "directory object has empty path");
@@ -71,7 +71,7 @@
         std::string path;
 
         if (duk_is_string(ctx, pattern_index))
-            path = fs_util::find(base, dukx_get_std_string(ctx, pattern_index), recursive);
+            path = fs_util::find(base, dukx_get_string(ctx, pattern_index), recursive);
         else {
             // Check if it's a valid RegExp object.
             duk_get_global_string(ctx, "RegExp");
@@ -91,7 +91,7 @@
         if (path.empty())
             return 0;
 
-        dukx_push_std_string(ctx, path);
+        dukx_push_string(ctx, path);
     } catch (const std::exception& ex) {
         duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
     }
@@ -199,7 +199,7 @@
         unsigned i = 0;
         for (const auto& entry : boost::filesystem::directory_iterator(path)) {
             duk_push_object(ctx);
-            dukx_push_std_string(ctx, entry.path().filename().string());
+            dukx_push_string(ctx, entry.path().filename().string());
             duk_put_prop_string(ctx, -2, "name");
             duk_push_int(ctx, entry.status().type());
             duk_put_prop_string(ctx, -2, "type");
@@ -210,7 +210,7 @@
 
         // 'path' property.
         duk_push_string(ctx, "path");
-        dukx_push_std_string(ctx, path);
+        dukx_push_string(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()));
@@ -305,7 +305,7 @@
 
 void directory_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    StackAssert sa(plugin->context());
+    dukx_stack_assert sa(plugin->context());
 
     duk_get_global_string(plugin->context(), "Irccd");
     duk_push_c_function(plugin->context(), constructor, 2);
--- a/libirccd-js/irccd/js/duktape.hpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/libirccd-js/irccd/js/duktape.hpp	Mon Nov 27 10:16:45 2017 +0100
@@ -16,8 +16,8 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#ifndef IRCCD_DUKTAPE_HPP
-#define IRCCD_DUKTAPE_HPP
+#ifndef IRCCD_JS_DUKTAPE_HPP
+#define IRCCD_JS_DUKTAPE_HPP
 
 /**
  * \file duktape.hpp
@@ -25,13 +25,8 @@
  * \author David Demelier <markand@malikania.fr>
  */
 
-#include <cerrno>
 #include <cstdio>
 #include <cstdlib>
-#include <cstring>
-#include <exception>
-#include <fstream>
-#include <iterator>
 #include <memory>
 #include <string>
 #include <unordered_map>
@@ -45,19 +40,20 @@
 /**
  * \brief Stack sanity checker.
  *
- * Instanciate this class where you need to manipulate the Duktape stack outside a Duktape/C function, its destructor
- * will examinate if the stack size matches the user expected size.
+ * Instanciate this class where you need to manipulate the Duktape stack outside
+ * a Duktape/C function, its destructor will examinate if the stack size matches
+ * the user expected size.
  *
  * When compiled with NDEBUG, this class does nothing.
  *
  * To use it, just declare an lvalue at the beginning of your function.
  */
-class StackAssert {
+class dukx_stack_assert {
 #if !defined(NDEBUG)
 private:
-    duk_context *m_context;
-    unsigned m_expected;
-    unsigned m_begin;
+    duk_context* context_;
+    unsigned expected_;
+    int at_start_;
 #endif
 
 public:
@@ -69,11 +65,11 @@
      * \param ctx the context
      * \param expected the size expected relative to the already existing values
      */
-    inline StackAssert(duk_context *ctx, unsigned expected = 0) noexcept
+    inline dukx_stack_assert(duk_context* ctx, unsigned expected = 0) noexcept
 #if !defined(NDEBUG)
-        : m_context(ctx)
-        , m_expected(expected)
-        , m_begin(static_cast<unsigned>(duk_get_top(ctx)))
+        : context_(ctx)
+        , expected_(expected)
+        , at_start_(duk_get_top(ctx))
 #endif
     {
 #if defined(NDEBUG)
@@ -87,16 +83,18 @@
      *
      * No-op if NDEBUG is set.
      */
-    inline ~StackAssert() noexcept
+    inline ~dukx_stack_assert() noexcept
     {
 #if !defined(NDEBUG)
-        if (static_cast<unsigned>(duk_get_top(m_context)) - m_begin != m_expected) {
-            std::fprintf(stderr, "Corrupt stack detection in StackAssert:\n");
-            std::fprintf(stderr, "  Size at start:            %u\n", m_begin);
-            std::fprintf(stderr, "  Size at end:              %d\n", duk_get_top(m_context));
-            std::fprintf(stderr, "  Expected (user):          %u\n", m_expected);
-            std::fprintf(stderr, "  Expected (adjusted):      %u\n", m_expected + m_begin);
-            std::fprintf(stderr, "  Number of stale values:   %u\n", duk_get_top(m_context) - m_begin - m_expected);
+        auto result = duk_get_top(context_) - at_start_;
+
+        if (result != static_cast<int>(expected_)) {
+            std::fprintf(stderr, "Corrupt stack detection in dukx_stack_assert:\n");
+            std::fprintf(stderr, "  Size at start:           %d\n", at_start_);
+            std::fprintf(stderr, "  Size at end:             %d\n", duk_get_top(context_));
+            std::fprintf(stderr, "  Expected (user):         %u\n", expected_);
+            std::fprintf(stderr, "  Expected (adjusted):     %u\n", expected_ + at_start_);
+            std::fprintf(stderr, "  Difference count:       %+d\n", result - expected_);
             std::abort();
         }
 #endif
@@ -108,20 +106,20 @@
  *
  * This class fills the fields got in an Error object.
  */
-class Exception : public std::exception {
+class dukx_exception : public std::exception {
 public:
-    std::string name;       //!< name of error
-    std::string message;    //!< error message
-    std::string stack;      //!< stack if available
-    std::string fileName;   //!< filename if applicable
-    int lineNumber{0};      //!< line number if applicable
+    std::string name;               //!< name of error
+    std::string message;            //!< error message
+    std::string stack;              //!< stack if available
+    std::string file_name;          //!< filename if applicable
+    int line_number{0};             //!< line number if applicable
 
     /**
      * Get the error message. This effectively returns message field.
      *
      * \return the message
      */
-    const char *what() const noexcept override
+    const char* what() const noexcept override
     {
         return message.c_str();
     }
@@ -132,21 +130,18 @@
  *
  * This class is implicitly convertible to duk_context for convenience.
  */
-class UniqueContext {
+class dukx_context {
 private:
-    using Deleter = void (*)(duk_context *);
-    using Handle = std::unique_ptr<duk_context, Deleter>;
+    std::unique_ptr<duk_context, void (*)(duk_context*)> m_handle;
 
-    Handle m_handle;
-
-    UniqueContext(const UniqueContext &) = delete;
-    UniqueContext &operator=(const UniqueContext &) = delete;
+    dukx_context(const dukx_context&) = delete;
+    dukx_context &operator=(const dukx_context&) = delete;
 
 public:
     /**
      * Create default context.
      */
-    inline UniqueContext()
+    inline dukx_context() noexcept
         : m_handle(duk_create_heap_default(), duk_destroy_heap)
     {
     }
@@ -154,14 +149,14 @@
     /**
      * Default move constructor.
      */
-    UniqueContext(UniqueContext &&) noexcept = default;
+    dukx_context(dukx_context&&) noexcept = default;
 
     /**
      * Convert the context to the native Duktape/C type.
      *
      * \return the duk_context
      */
-    inline operator duk_context *() noexcept
+    inline operator duk_context*() noexcept
     {
         return m_handle.get();
     }
@@ -171,7 +166,7 @@
      *
      * \return the duk_context
      */
-    inline operator duk_context *() const noexcept
+    inline operator duk_context*() const noexcept
     {
         return m_handle.get();
     }
@@ -181,161 +176,21 @@
      *
      * \return this
      */
-    UniqueContext &operator=(UniqueContext &&) noexcept = delete;
-};
-
-/**
- * \brief Base ECMAScript error class.
- * \warning Override the function create for your own exceptions
- */
-class Error {
-private:
-    int m_type{DUK_ERR_ERROR};
-    std::string m_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 Error(int type, std::string message) noexcept
-        : m_type(type)
-        , m_message(std::move(message))
-    {
-    }
-
-public:
-    /**
-     * Constructor with a message.
-     *
-     * \param message the message
-     */
-    inline Error(std::string message) noexcept
-        : m_message(std::move(message))
-    {
-    }
-
-    /**
-     * Create the exception on the stack.
-     *
-     * \note the default implementation search for the global variables
-     * \param ctx the context
-     */
-    virtual void raise(duk_context *ctx) const
-    {
-        duk_error(ctx, m_type, "%s", m_message.c_str());
-    }
-};
-
-/**
- * \brief Error in eval() function.
- */
-class EvalError : public Error {
-public:
-    /**
-     * Construct an EvalError.
-     *
-     * \param message the message
-     */
-    inline EvalError(std::string message) noexcept
-        : Error(DUK_ERR_EVAL_ERROR, std::move(message))
-    {
-    }
+    dukx_context& operator=(dukx_context&&) noexcept = delete;
 };
 
 /**
- * \brief Value is out of range.
- */
-class RangeError : public Error {
-public:
-    /**
-     * Construct an RangeError.
-     *
-     * \param message the message
-     */
-    inline RangeError(std::string message) noexcept
-        : Error(DUK_ERR_RANGE_ERROR, std::move(message))
-    {
-    }
-};
-
-/**
- * \brief Trying to use a variable that does not exist.
- */
-class ReferenceError : public Error {
-public:
-    /**
-     * Construct an ReferenceError.
-     *
-     * \param message the message
-     */
-    inline ReferenceError(std::string message) noexcept
-        : Error(DUK_ERR_REFERENCE_ERROR, std::move(message))
-    {
-    }
-};
-
-/**
- * \brief Syntax error in the script.
- */
-class SyntaxError : public Error {
-public:
-    /**
-     * Construct an SyntaxError.
-     *
-     * \param message the message
-     */
-    inline SyntaxError(std::string message) noexcept
-        : Error(DUK_ERR_SYNTAX_ERROR, std::move(message))
-    {
-    }
-};
-
-/**
- * \brief Invalid type given.
- */
-class TypeError : public Error {
-public:
-    /**
-     * Construct an TypeError.
-     *
-     * \param message the message
-     */
-    inline TypeError(std::string message) noexcept
-        : Error(DUK_ERR_TYPE_ERROR, std::move(message))
-    {
-    }
-};
-
-/**
- * \brief URI manipulation failure.
- */
-class URIError : public Error {
-public:
-    /**
-     * Construct an URIError.
-     *
-     * \param message the message
-     */
-    inline URIError(std::string message) noexcept
-        : Error(DUK_ERR_URI_ERROR, std::move(message))
-    {
-    }
-};
-
-/**
- * Get the error object when a JavaScript error has been thrown (e.g. eval failure).
+ * Get the error object when a JavaScript error has been thrown (e.g. eval
+ * failure).
  *
  * \param ctx the context
  * \param index the index
  * \param pop if true, also remove the exception from the stack
  * \return the information
  */
-inline Exception dukx_exception(duk_context *ctx, int index, bool pop = true)
+inline dukx_exception dukx_get_exception(duk_context* ctx, int index, bool pop = true)
 {
-    Exception ex;
+    dukx_exception ex;
 
     index = duk_normalize_index(ctx, index);
 
@@ -344,9 +199,9 @@
     duk_get_prop_string(ctx, index, "message");
     ex.message = duk_to_string(ctx, -1);
     duk_get_prop_string(ctx, index, "fileName");
-    ex.fileName = duk_to_string(ctx, -1);
+    ex.file_name = duk_to_string(ctx, -1);
     duk_get_prop_string(ctx, index, "lineNumber");
-    ex.lineNumber = duk_to_int(ctx, -1);
+    ex.line_number = duk_to_int(ctx, -1);
     duk_get_prop_string(ctx, index, "stack");
     ex.stack = duk_to_string(ctx, -1);
     duk_pop_n(ctx, 5);
@@ -358,25 +213,44 @@
 }
 
 /**
- * Enumerate an object or an array at the specified index.
+ * Get a string, return 0 if not a string.
+ *
+ * \param ctx the context
+ * \param index the index
+ * \return the string
+ */
+inline std::string dukx_get_string(duk_context* ctx, int index)
+{
+    duk_size_t size;
+    const char* text = duk_get_lstring(ctx, index, &size);
+
+    return std::string(text, size);
+}
+
+/**
+ * Require a string, throws a JavaScript exception if not a string.
  *
  * \param ctx the context
- * \param index the object or array index
- * \param flags the optional flags to pass to duk_enum
- * \param getvalue set to true if you want to extract the value
- * \param func the function to call for each properties
+ * \param index the index
+ * \return the string
  */
-template <typename Func>
-void dukx_enumerate(duk_context *ctx, int index, duk_uint_t flags, duk_bool_t getvalue, Func &&func)
+inline std::string dukx_require_string(duk_context* ctx, int index)
 {
-    duk_enum(ctx, index, flags);
+    duk_size_t size;
+    const char* text = duk_require_lstring(ctx, index, &size);
+
+    return std::string(text, size);
+}
 
-    while (duk_next(ctx, -1, getvalue)) {
-        func(ctx);
-        duk_pop_n(ctx, 1 + (getvalue ? 1 : 0));
-    }
-
-    duk_pop(ctx);
+/**
+ * Push a C++ string.
+ *
+ * \param ctx the context
+ * \param str the string
+ */
+inline void dukx_push_string(duk_context* ctx, const std::string& str)
+{
+    duk_push_lstring(ctx, str.data(), str.length());
 }
 
 /**
@@ -391,122 +265,6 @@
     ex.raise(ctx);
 }
 
-/**
- * Get a string, return 0 if not a string.
- *
- * \param ctx the context
- * \param index the index
- * \return the string
- */
-inline std::string dukx_get_std_string(duk_context *ctx, int index)
-{
-    duk_size_t size;
-    const char *text = duk_get_lstring(ctx, index, &size);
-
-    return std::string(text, size);
-}
-
-/**
- * Require a string, throws a JavaScript exception if not a string.
- *
- * \param ctx the context
- * \param index the index
- * \return the string
- */
-inline std::string dukx_require_std_string(duk_context *ctx, int index)
-{
-    duk_size_t size;
-    const char *text = duk_require_lstring(ctx, index, &size);
-
-    return std::string(text, size);
-}
-
-/**
- * Push a C++ string.
- *
- * \param ctx the context
- * \param str the string
- */
-inline void dukx_push_std_string(duk_context *ctx, const std::string &str)
-{
-    duk_push_lstring(ctx, str.data(), str.length());
-}
-
-/**
- * Get an array.
- *
- * \param ctx the context
- * \param index the array index
- * \param get the conversion function (e.g. duk_get_int)
- */
-template <typename Getter>
-auto dukx_get_array(duk_context *ctx, duk_idx_t index, Getter &&get)
-{
-    using T = decltype(get(ctx, 0));
-
-    std::vector<T> result;
-    std::size_t length = duk_get_length(ctx, index);
-
-    for (std::size_t i = 0; i < length; ++i) {
-        duk_get_prop_index(ctx, -1, i);
-        result.push_back(get(ctx, -1));
-        duk_pop(ctx);
-    }
-
-    return result;
-}
-
-/**
- * Push an array.
- *
- * \param ctx the context
- * \param values the values
- * \param push the function to push values
- */
-template <typename T, typename Pusher>
-void dukx_push_array(duk_context *ctx, const std::vector<T> &values, Pusher &&push)
-{
-    duk_push_array(ctx);
-
-    int i = 0;
-    for (auto x : values) {
-        push(ctx, x);
-        duk_put_prop_index(ctx, -2, i++);
-    }
-}
-
-/**
- * Replace the removed duk_peval_string with one throwing exception.
- *
- * \param ctx the context
- * \param path the path to the file
- * \throw Exception on errors
- */
-inline void dukx_peval_file(duk_context* ctx, const std::string& path)
-{
-    std::ifstream input(path);
-
-    if (!input) {
-        Exception ex;
-
-        ex.name = "Error";
-        ex.message = std::strerror(errno);
-        ex.fileName = path;
-
-        throw ex;
-    }
-
-    std::string data(std::istreambuf_iterator<char>(input), {});
-
-    if (duk_peval_lstring(ctx, data.c_str(), data.length()) != 0) {
-        auto ex = dukx_exception(ctx, -1);
-
-        ex.fileName = path;
-
-        throw ex;
-    }
-}
-
 } // !irccd
 
-#endif // !IRCCD_DUKTAPE_HPP
+#endif // !MALIKANIA_COMMON_DUKTAPE_HPP
--- a/libirccd-js/irccd/js/elapsed_timer_jsapi.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/libirccd-js/irccd/js/elapsed_timer_jsapi.cpp	Mon Nov 27 10:16:45 2017 +0100
@@ -29,7 +29,7 @@
 
 boost::timer::cpu_timer* self(duk_context* ctx)
 {
-    StackAssert sa(ctx);
+    dukx_stack_assert sa(ctx);
 
     duk_push_this(ctx);
     duk_get_prop_string(ctx, -1, signature);
@@ -132,7 +132,7 @@
 
 void elapsed_timer_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    StackAssert sa(plugin->context());
+    dukx_stack_assert sa(plugin->context());
 
     duk_get_global_string(plugin->context(), "Irccd");
     duk_push_c_function(plugin->context(), constructor, 0);
--- a/libirccd-js/irccd/js/file_jsapi.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/libirccd-js/irccd/js/file_jsapi.cpp	Mon Nov 27 10:16:45 2017 +0100
@@ -53,7 +53,7 @@
 
 void push_stat(duk_context* ctx, const struct stat& st)
 {
-    StackAssert sa(ctx, 1);
+    dukx_stack_assert sa(ctx, 1);
 
     duk_push_object(ctx);
 
@@ -124,7 +124,7 @@
 
 file* self(duk_context* ctx)
 {
-    StackAssert sa(ctx);
+    dukx_stack_assert sa(ctx);
 
     duk_push_this(ctx);
     duk_get_prop_string(ctx, -1, signature);
@@ -153,7 +153,7 @@
  */
 duk_ret_t method_basename(duk_context* ctx)
 {
-    dukx_push_std_string(ctx, fs_util::base_name(self(ctx)->path()));
+    dukx_push_string(ctx, fs_util::base_name(self(ctx)->path()));
 
     return 1;
 }
@@ -182,7 +182,7 @@
  */
 duk_ret_t method_dirname(duk_context* ctx)
 {
-    dukx_push_std_string(ctx, fs_util::dir_name(self(ctx)->path()));
+    dukx_push_string(ctx, fs_util::dir_name(self(ctx)->path()));
 
     return 1;
 }
@@ -213,7 +213,7 @@
         auto pos = buffer.find('\n');
 
         if (pos != std::string::npos) {
-            dukx_push_std_string(ctx, clear_crlf(buffer.substr(0, pos)));
+            dukx_push_string(ctx, clear_crlf(buffer.substr(0, pos)));
             duk_put_prop_index(ctx, -2, i++);
 
             buffer.erase(0, pos + 1);
@@ -226,7 +226,7 @@
 
     // Missing '\n' in end of file.
     if (!buffer.empty()) {
-        dukx_push_std_string(ctx, clear_crlf(buffer));
+        dukx_push_string(ctx, clear_crlf(buffer));
         duk_put_prop_index(ctx, -2, i++);
     }
 
@@ -279,7 +279,7 @@
             data.resize(total);
         }
 
-        dukx_push_std_string(ctx, data);
+        dukx_push_string(ctx, data);
     } catch (const std::exception&) {
         dukx_throw(ctx, system_error());
     }
@@ -310,7 +310,7 @@
     if (std::ferror(fp))
         dukx_throw(ctx, system_error());
 
-    dukx_push_std_string(ctx, clear_crlf(result));
+    dukx_push_string(ctx, clear_crlf(result));
 
     return 1;
 }
@@ -519,7 +519,7 @@
  */
 duk_ret_t function_basename(duk_context* ctx)
 {
-    dukx_push_std_string(ctx, fs_util::base_name(duk_require_string(ctx, 0)));
+    dukx_push_string(ctx, fs_util::base_name(duk_require_string(ctx, 0)));
 
     return 1;
 }
@@ -537,7 +537,7 @@
  */
 duk_ret_t function_dirname(duk_context* ctx)
 {
-    dukx_push_std_string(ctx, fs_util::dir_name(duk_require_string(ctx, 0)));
+    dukx_push_string(ctx, fs_util::dir_name(duk_require_string(ctx, 0)));
 
     return 1;
 }
@@ -641,7 +641,7 @@
 
 void file_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    StackAssert sa(plugin->context());
+    dukx_stack_assert sa(plugin->context());
 
     duk_get_global_string(plugin->context(), "Irccd");
     duk_push_c_function(plugin->context(), constructor, 2);
@@ -663,7 +663,7 @@
     assert(ctx);
     assert(fp);
 
-    StackAssert sa(ctx);
+    dukx_stack_assert sa(ctx);
 
     duk_push_this(ctx);
     duk_push_pointer(ctx, fp);
@@ -676,7 +676,7 @@
     assert(ctx);
     assert(fp);
 
-    StackAssert sa(ctx, 1);
+    dukx_stack_assert sa(ctx, 1);
 
     duk_push_object(ctx);
     duk_push_pointer(ctx, fp);
--- a/libirccd-js/irccd/js/irccd_jsapi.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/libirccd-js/irccd/js/irccd_jsapi.cpp	Mon Nov 27 10:16:45 2017 +0100
@@ -160,13 +160,13 @@
 
 void system_error::raise(duk_context *ctx) const
 {
-    StackAssert sa(ctx, 0);
+    dukx_stack_assert sa(ctx, 0);
 
     duk_get_global_string(ctx, "Irccd");
     duk_get_prop_string(ctx, -1, "SystemError");
     duk_remove(ctx, -2);
     duk_push_int(ctx, errno_);
-    dukx_push_std_string(ctx, message_);
+    dukx_push_string(ctx, message_);
     duk_new(ctx, 2);
     duk_throw(ctx);
 }
@@ -178,7 +178,7 @@
 
 void irccd_jsapi::load(irccd& irccd, std::shared_ptr<js_plugin> plugin)
 {
-    StackAssert sa(plugin->context());
+    dukx_stack_assert sa(plugin->context());
 
     // irccd.
     duk_push_object(plugin->context());
@@ -220,7 +220,7 @@
 
 irccd& dukx_get_irccd(duk_context *ctx)
 {
-    StackAssert sa(ctx);
+    dukx_stack_assert sa(ctx);
 
     duk_get_global_string(ctx, "\xff""\xff""irccd-ref");
     auto ptr = static_cast<irccd*>(duk_to_pointer(ctx, -1));
--- a/libirccd-js/irccd/js/js_plugin.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/libirccd-js/irccd/js/js_plugin.cpp	Mon Nov 27 10:16:45 2017 +0100
@@ -35,26 +35,30 @@
 
 std::unordered_map<std::string, std::string> js_plugin::get_table(const std::string& name) const
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
     std::unordered_map<std::string, std::string> result;
 
     duk_get_global_string(context_, name.c_str());
-    dukx_enumerate(context_, -1, 0, true, [&] (auto ctx) {
-        result.emplace(duk_to_string(ctx, -2), duk_to_string(ctx, -1));
-    });
-    duk_pop(context_);
+    duk_enum(context_, -1, 0);
+
+    while (duk_next(context_, -1, true)) {
+        result.emplace(duk_to_string(context_, -2), duk_to_string(context_, -1));
+        duk_pop_n(context_, 2);
+    }
+
+    duk_pop_n(context_, 2);
 
     return result;
 }
 
 void js_plugin::put_table(const std::string& name, const std::unordered_map<std::string, std::string>& vars)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     duk_get_global_string(context_, name.c_str());
 
-    for (const auto &pair : vars) {
-        dukx_push_std_string(context_, pair.second);
+    for (const auto& pair : vars) {
+        dukx_push_string(context_, pair.second);
         duk_put_prop_string(context_, -2, pair.first.c_str());
     }
 
@@ -73,7 +77,7 @@
         duk_insert(context_, -nargs - 1);
 
         if (duk_pcall(context_, nargs) != 0)
-            throw dukx_exception(context_, -1, true);
+            throw dukx_get_exception(context_, -1, true);
 
         duk_pop(context_);
     }
@@ -82,7 +86,7 @@
 js_plugin::js_plugin(std::string name, std::string path)
     : plugin(name, path)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     /*
      * Create two special tables for configuration and formats, they are
@@ -103,9 +107,9 @@
 
     duk_push_pointer(context_, this);
     duk_put_global_string(context_, "\xff""\xff""plugin");
-    dukx_push_std_string(context_, name);
+    dukx_push_string(context_, name);
     duk_put_global_string(context_, "\xff""\xff""name");
-    dukx_push_std_string(context_, path);
+    dukx_push_string(context_, path);
     duk_put_global_string(context_, "\xff""\xff""path");
 }
 
@@ -122,7 +126,7 @@
     );
 
     if (duk_peval_string(context_, data.c_str()))
-        throw dukx_exception(context_, -1);
+        throw dukx_get_exception(context_, -1);
 
     // Read metadata.
     duk_get_global_string(context_, "info");
@@ -144,41 +148,41 @@
 
 void js_plugin::on_channel_mode(irccd& , const channel_mode_event &event)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_std_string(context_, event.origin);
-    dukx_push_std_string(context_, event.channel);
-    dukx_push_std_string(context_, event.mode);
-    dukx_push_std_string(context_, event.argument);
+    dukx_push_string(context_, event.origin);
+    dukx_push_string(context_, event.channel);
+    dukx_push_string(context_, event.mode);
+    dukx_push_string(context_, event.argument);
     call("onChannelMode", 5);
 }
 
 void js_plugin::on_channel_notice(irccd& , const channel_notice_event &event)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_std_string(context_, event.origin);
-    dukx_push_std_string(context_, event.channel);
-    dukx_push_std_string(context_, event.message);
+    dukx_push_string(context_, event.origin);
+    dukx_push_string(context_, event.channel);
+    dukx_push_string(context_, event.message);
     call("onChannelNotice", 4);
 }
 
 void js_plugin::on_command(irccd& , const message_event &event)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_std_string(context_, event.origin);
-    dukx_push_std_string(context_, event.channel);
-    dukx_push_std_string(context_, event.message);
+    dukx_push_string(context_, event.origin);
+    dukx_push_string(context_, event.channel);
+    dukx_push_string(context_, event.message);
     call("onCommand", 4);
 }
 
 void js_plugin::on_connect(irccd& , const connect_event &event)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
     call("onConnect", 1);
@@ -186,177 +190,190 @@
 
 void js_plugin::on_invite(irccd& , const invite_event &event)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_std_string(context_, event.origin);
-    dukx_push_std_string(context_, event.channel);
+    dukx_push_string(context_, event.origin);
+    dukx_push_string(context_, event.channel);
     call("onInvite", 3);
 }
 
 void js_plugin::on_join(irccd& , const join_event &event)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_std_string(context_, event.origin);
-    dukx_push_std_string(context_, event.channel);
+    dukx_push_string(context_, event.origin);
+    dukx_push_string(context_, event.channel);
     call("onJoin", 3);
 }
 
 void js_plugin::on_kick(irccd& , const kick_event &event)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_std_string(context_, event.origin);
-    dukx_push_std_string(context_, event.channel);
-    dukx_push_std_string(context_, event.target);
-    dukx_push_std_string(context_, event.reason);
+    dukx_push_string(context_, event.origin);
+    dukx_push_string(context_, event.channel);
+    dukx_push_string(context_, event.target);
+    dukx_push_string(context_, event.reason);
     call("onKick", 5);
 }
 
 void js_plugin::on_load(irccd&)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     call("onLoad", 0);
 }
 
 void js_plugin::on_message(irccd& , const message_event &event)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_std_string(context_, event.origin);
-    dukx_push_std_string(context_, event.channel);
-    dukx_push_std_string(context_, event.message);
+    dukx_push_string(context_, event.origin);
+    dukx_push_string(context_, event.channel);
+    dukx_push_string(context_, event.message);
     call("onMessage", 4);
 }
 
 void js_plugin::on_me(irccd& , const me_event &event)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_std_string(context_, event.origin);
-    dukx_push_std_string(context_, event.channel);
-    dukx_push_std_string(context_, event.message);
+    dukx_push_string(context_, event.origin);
+    dukx_push_string(context_, event.channel);
+    dukx_push_string(context_, event.message);
     call("onMe", 4);
 }
 
 void js_plugin::on_mode(irccd& , const mode_event &event)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_std_string(context_, event.origin);
-    dukx_push_std_string(context_, event.mode);
+    dukx_push_string(context_, event.origin);
+    dukx_push_string(context_, event.mode);
     call("onMode", 3);
 }
 
 void js_plugin::on_names(irccd& , const names_event &event)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_std_string(context_, event.channel);
-    dukx_push_array(context_, event.names, dukx_push_std_string);
+    dukx_push_string(context_, event.channel);
+    duk_push_array(context_);
+
+    for (unsigned i = 0; i < event.names.size(); ++i) {
+        dukx_push_string(context_, event.names[i]);
+        duk_put_prop_index(context_, -2, i);
+    }
+
     call("onNames", 3);
 }
 
 void js_plugin::on_nick(irccd& , const nick_event &event)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_std_string(context_, event.origin);
-    dukx_push_std_string(context_, event.nickname);
+    dukx_push_string(context_, event.origin);
+    dukx_push_string(context_, event.nickname);
     call("onNick", 3);
 }
 
 void js_plugin::on_notice(irccd& , const notice_event &event)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_std_string(context_, event.origin);
-    dukx_push_std_string(context_, event.message);
+    dukx_push_string(context_, event.origin);
+    dukx_push_string(context_, event.message);
     call("onNotice", 3);
 }
 
 void js_plugin::on_part(irccd& , const part_event &event)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_std_string(context_, event.origin);
-    dukx_push_std_string(context_, event.channel);
-    dukx_push_std_string(context_, event.reason);
+    dukx_push_string(context_, event.origin);
+    dukx_push_string(context_, event.channel);
+    dukx_push_string(context_, event.reason);
     call("onPart", 4);
 }
 
 void js_plugin::on_query(irccd& , const query_event &event)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_std_string(context_, event.origin);
-    dukx_push_std_string(context_, event.message);
+    dukx_push_string(context_, event.origin);
+    dukx_push_string(context_, event.message);
     call("onQuery", 3);
 }
 
 void js_plugin::on_query_command(irccd& , const query_event &event)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_std_string(context_, event.origin);
-    dukx_push_std_string(context_, event.message);
+    dukx_push_string(context_, event.origin);
+    dukx_push_string(context_, event.message);
     call("onQueryCommand", 3);
 }
 
 void js_plugin::on_reload(irccd& )
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     call("onReload");
 }
 
 void js_plugin::on_topic(irccd& , const topic_event &event)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_std_string(context_, event.origin);
-    dukx_push_std_string(context_, event.channel);
-    dukx_push_std_string(context_, event.topic);
+    dukx_push_string(context_, event.origin);
+    dukx_push_string(context_, event.channel);
+    dukx_push_string(context_, event.topic);
     call("onTopic", 4);
 }
 
 void js_plugin::on_unload(irccd& )
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     call("onUnload");
 }
 
 void js_plugin::on_whois(irccd& , const whois_event &event)
 {
-    StackAssert sa(context_);
+    dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
     duk_push_object(context_);
-    dukx_push_std_string(context_, event.whois.nick);
+    dukx_push_string(context_, event.whois.nick);
     duk_put_prop_string(context_, -2, "nickname");
-    dukx_push_std_string(context_, event.whois.user);
+    dukx_push_string(context_, event.whois.user);
     duk_put_prop_string(context_, -2, "username");
-    dukx_push_std_string(context_, event.whois.realname);
+    dukx_push_string(context_, event.whois.realname);
     duk_put_prop_string(context_, -2, "realname");
-    dukx_push_std_string(context_, event.whois.host);
+    dukx_push_string(context_, event.whois.host);
     duk_put_prop_string(context_, -2, "host");
-    dukx_push_array(context_, event.whois.channels, dukx_push_std_string);
+    duk_push_array(context_);
+
+    for (unsigned i = 0; i < event.whois.channels.size(); ++i) {
+        dukx_push_string(context_, event.whois.channels[i]);
+        duk_put_prop_index(context_, -2, 1);
+    }
+
     duk_put_prop_string(context_, -2, "channels");
+
     call("onWhois", 2);
 }
 
--- a/libirccd-js/irccd/js/js_plugin.hpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/libirccd-js/irccd/js/js_plugin.hpp	Mon Nov 27 10:16:45 2017 +0100
@@ -56,7 +56,7 @@
 
 private:
     // JavaScript context
-    UniqueContext context_;
+    dukx_context context_;
 
     // Private helpers.
     std::unordered_map<std::string, std::string> get_table(const std::string&) const;
@@ -77,7 +77,7 @@
      *
      * \return the context
      */
-    inline UniqueContext& context() noexcept
+    inline dukx_context& context() noexcept
     {
         return context_;
     }
--- a/libirccd-js/irccd/js/logger_jsapi.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/libirccd-js/irccd/js/logger_jsapi.cpp	Mon Nov 27 10:16:45 2017 +0100
@@ -91,7 +91,7 @@
 
 void logger_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    StackAssert sa(plugin->context());
+    dukx_stack_assert sa(plugin->context());
 
     duk_get_global_string(plugin->context(), "Irccd");
     duk_push_object(plugin->context());
--- a/libirccd-js/irccd/js/plugin_jsapi.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/libirccd-js/irccd/js/plugin_jsapi.cpp	Mon Nov 27 10:16:45 2017 +0100
@@ -46,10 +46,10 @@
 
     try {
         func(dukx_get_irccd(ctx), name);
-    } catch (const std::out_of_range &ex) {
-        dukx_throw(ctx, ReferenceError(ex.what()));
+    } catch (const std::out_of_range& ex) {
+        (void)duk_error(ctx, DUK_ERR_REFERENCE_ERROR, "%s", ex.what());
     } catch (const std::exception &ex) {
-        dukx_throw(ctx, Error(ex.what()));
+        (void)duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
     }
 
     return nret;
@@ -222,15 +222,15 @@
         return 0;
 
     duk_push_object(ctx);
-    dukx_push_std_string(ctx, plugin->name());
+    dukx_push_string(ctx, plugin->name());
     duk_put_prop_string(ctx, -2, "name");
-    dukx_push_std_string(ctx, plugin->author());
+    dukx_push_string(ctx, plugin->author());
     duk_put_prop_string(ctx, -2, "author");
-    dukx_push_std_string(ctx, plugin->license());
+    dukx_push_string(ctx, plugin->license());
     duk_put_prop_string(ctx, -2, "license");
-    dukx_push_std_string(ctx, plugin->summary());
+    dukx_push_string(ctx, plugin->summary());
     duk_put_prop_string(ctx, -2, "summary");
-    dukx_push_std_string(ctx, plugin->version());
+    dukx_push_string(ctx, plugin->version());
     duk_put_prop_string(ctx, -2, "version");
 
     return 1;
@@ -247,9 +247,14 @@
  */
 duk_idx_t list(duk_context* ctx)
 {
-    dukx_push_array(ctx, dukx_get_irccd(ctx).plugins().list(), [] (auto ctx, auto plugin) {
-        dukx_push_std_string(ctx, plugin->name());
-    });
+    int i = 0;
+
+    duk_push_array(ctx);
+
+    for (const auto& p : dukx_get_irccd(ctx).plugins().list()) {
+        dukx_push_string(ctx, p->name());
+        duk_put_prop_index(ctx, -2, i++);
+    }
 
     return 1;
 }
@@ -269,7 +274,7 @@
  */
 duk_idx_t load(duk_context* ctx)
 {
-    return wrap(ctx, 0, [&] (irccd &irccd, const std::string &name) {
+    return wrap(ctx, 0, [&] (irccd& irccd, const std::string& name) {
         irccd.plugins().load(name);
     });
 }
@@ -288,7 +293,7 @@
  */
 duk_idx_t reload(duk_context* ctx)
 {
-    return wrap(ctx, 0, [&] (irccd &irccd, const std::string &name) {
+    return wrap(ctx, 0, [&] (irccd& irccd, const std::string& name) {
         irccd.plugins().reload(name);
     });
 }
@@ -330,7 +335,7 @@
 
 void plugin_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    StackAssert sa(plugin->context());
+    dukx_stack_assert sa(plugin->context());
 
     duk_push_pointer(plugin->context(), new std::weak_ptr<js_plugin>(plugin));
     duk_push_object(plugin->context());
@@ -373,7 +378,7 @@
 
 std::shared_ptr<js_plugin> dukx_get_plugin(duk_context* ctx)
 {
-    StackAssert sa(ctx);
+    dukx_stack_assert sa(ctx);
 
     duk_get_global_string(ctx, plugin_ref);
     auto plugin = static_cast<std::weak_ptr<js_plugin>*>(duk_to_pointer(ctx, -1));
--- a/libirccd-js/irccd/js/server_jsapi.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/libirccd-js/irccd/js/server_jsapi.cpp	Mon Nov 27 10:16:45 2017 +0100
@@ -36,7 +36,7 @@
 
 std::shared_ptr<server> self(duk_context* ctx)
 {
-    StackAssert sa(ctx);
+    dukx_stack_assert sa(ctx);
 
     duk_push_this(ctx);
     duk_get_prop_string(ctx, -1, signature);
@@ -101,9 +101,9 @@
     auto server = self(ctx);
 
     duk_push_object(ctx);
-    dukx_push_std_string(ctx, server->name());
+    dukx_push_string(ctx, server->name());
     duk_put_prop_string(ctx, -2, "name");
-    dukx_push_std_string(ctx, server->host());
+    dukx_push_string(ctx, server->host());
     duk_put_prop_string(ctx, -2, "host");
     duk_push_int(ctx, server->port());
     duk_put_prop_string(ctx, -2, "port");
@@ -111,17 +111,23 @@
     duk_put_prop_string(ctx, -2, "ssl");
     duk_push_boolean(ctx, server->flags() & server::ssl_verify);
     duk_put_prop_string(ctx, -2, "sslVerify");
-    dukx_push_std_string(ctx, server->command_char());
+    dukx_push_string(ctx, server->command_char());
     duk_put_prop_string(ctx, -2, "commandChar");
-    dukx_push_std_string(ctx, server->realname());
+    dukx_push_string(ctx, server->realname());
     duk_put_prop_string(ctx, -2, "realname");
-    dukx_push_std_string(ctx, server->nickname());
+    dukx_push_string(ctx, server->nickname());
     duk_put_prop_string(ctx, -2, "nickname");
-    dukx_push_std_string(ctx, server->username());
+    dukx_push_string(ctx, server->username());
     duk_put_prop_string(ctx, -2, "username");
-    dukx_push_array(ctx, server->channels(), [&] (auto ctx, auto channel) {
-        dukx_push_std_string(ctx, channel);
-    });
+
+    duk_push_array(ctx);
+
+    int i = 0;
+    for (const auto& c : server->channels()) {
+        dukx_push_string(ctx, c);
+        duk_put_prop_index(ctx, -2, i++);
+    }
+
     duk_put_prop_string(ctx, -2, "channels");
 
     return 1;
@@ -156,7 +162,7 @@
  */
 duk_ret_t join(duk_context* ctx)
 {
-    self(ctx)->join(duk_require_string(ctx, 0), dukx_get_std_string(ctx, 1));
+    self(ctx)->join(duk_require_string(ctx, 0), dukx_get_string(ctx, 1));
 
     return 0;
 }
@@ -174,7 +180,7 @@
  */
 duk_ret_t kick(duk_context* ctx)
 {
-    self(ctx)->kick(duk_require_string(ctx, 0), duk_require_string(ctx, 1), dukx_get_std_string(ctx, 2));
+    self(ctx)->kick(duk_require_string(ctx, 0), duk_require_string(ctx, 1), dukx_get_string(ctx, 2));
 
     return 0;
 }
@@ -290,7 +296,7 @@
  */
 duk_ret_t part(duk_context* ctx)
 {
-    self(ctx)->part(duk_require_string(ctx, 0), dukx_get_std_string(ctx, 1));
+    self(ctx)->part(duk_require_string(ctx, 0), dukx_get_string(ctx, 1));
 
     return 0;
 }
@@ -356,7 +362,7 @@
  */
 duk_ret_t toString(duk_context* ctx)
 {
-    dukx_push_std_string(ctx, self(ctx)->name());
+    dukx_push_string(ctx, self(ctx)->name());
 
     return 1;
 }
@@ -535,7 +541,7 @@
 
 void server_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    StackAssert sa(plugin->context());
+    dukx_stack_assert sa(plugin->context());
 
     duk_get_global_string(plugin->context(), "Irccd");
     duk_push_c_function(plugin->context(), constructor, 1);
@@ -556,7 +562,7 @@
     assert(ctx);
     assert(server);
 
-    StackAssert sa(ctx, 1);
+    dukx_stack_assert sa(ctx, 1);
 
     duk_push_object(ctx);
     duk_push_pointer(ctx, new std::shared_ptr<class server>(std::move(server)));
--- a/libirccd-js/irccd/js/system_jsapi.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/libirccd-js/irccd/js/system_jsapi.cpp	Mon Nov 27 10:16:45 2017 +0100
@@ -50,7 +50,7 @@
  */
 duk_ret_t env(duk_context* ctx)
 {
-    dukx_push_std_string(ctx, sys::env(dukx_get_std_string(ctx, 0)));
+    dukx_push_string(ctx, sys::env(dukx_get_string(ctx, 0)));
 
     return 1;
 }
@@ -82,7 +82,7 @@
  */
 duk_ret_t home(duk_context* ctx)
 {
-    dukx_push_std_string(ctx, sys::home());
+    dukx_push_string(ctx, sys::home());
 
     return 1;
 }
@@ -98,7 +98,7 @@
  */
 duk_ret_t name(duk_context* ctx)
 {
-    dukx_push_std_string(ctx, sys::name());
+    dukx_push_string(ctx, sys::name());
 
     return 1;
 }
@@ -202,7 +202,7 @@
  */
 duk_ret_t version(duk_context* ctx)
 {
-    dukx_push_std_string(ctx, sys::version());
+    dukx_push_string(ctx, sys::version());
 
     return 1;
 }
@@ -232,7 +232,7 @@
 
 void system_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    StackAssert sa(plugin->context());
+    dukx_stack_assert sa(plugin->context());
 
     duk_get_global_string(plugin->context(), "Irccd");
     duk_push_object(plugin->context());
--- a/libirccd-js/irccd/js/timer_jsapi.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/libirccd-js/irccd/js/timer_jsapi.cpp	Mon Nov 27 10:16:45 2017 +0100
@@ -86,7 +86,7 @@
 
     if (duk_pcall(ctx, 0)) {
         log::warning() << "plugin: " << plugin->name() << " timer error:" << std::endl;
-        log::warning() << "  " << dukx_exception(ctx, -1).what() << std::endl;
+        log::warning() << "  " << dukx_get_exception(ctx, -1).what() << std::endl;
     } else
         duk_pop(ctx);
 }
@@ -122,7 +122,7 @@
 
 timer* self(duk_context* ctx)
 {
-    StackAssert sa(ctx);
+    dukx_stack_assert sa(ctx);
 
     duk_push_this(ctx);
     duk_get_prop_string(ctx, -1, signature);
@@ -175,7 +175,7 @@
  */
 duk_ret_t destructor(duk_context* ctx)
 {
-    StackAssert sa(ctx);
+    dukx_stack_assert sa(ctx);
 
     // Get timer from this.
     duk_get_prop_string(ctx, 0, signature);
@@ -254,7 +254,7 @@
 
 void timer_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    StackAssert sa(plugin->context());
+    dukx_stack_assert sa(plugin->context());
 
     duk_get_global_string(plugin->context(), "Irccd");
     duk_push_c_function(plugin->context(), constructor, 3);
--- a/libirccd-js/irccd/js/unicode_jsapi.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/libirccd-js/irccd/js/unicode_jsapi.cpp	Mon Nov 27 10:16:45 2017 +0100
@@ -139,7 +139,7 @@
 
 void unicode_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    StackAssert sa(plugin->context());
+    dukx_stack_assert sa(plugin->context());
 
     duk_get_global_string(plugin->context(), "Irccd");
     duk_push_object(plugin->context());
--- a/libirccd-js/irccd/js/util_jsapi.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/libirccd-js/irccd/js/util_jsapi.cpp	Mon Nov 27 10:16:45 2017 +0100
@@ -46,12 +46,16 @@
     if (!duk_is_object(ctx, index))
         return params;
 
-    dukx_enumerate(ctx, index, 0, true, [&] (auto) {
-        if (dukx_get_std_string(ctx, -2) == "date")
+    duk_enum(ctx, index, 0);
+
+    while (duk_next(ctx, -1, true)) {
+        if (dukx_get_string(ctx, -2) == "date")
             params.time = static_cast<time_t>(duk_get_number(ctx, -1) / 1000);
         else
-            params.keywords.insert({dukx_get_std_string(ctx, -2), dukx_get_std_string(ctx, -1)});
-    });
+            params.keywords.insert({dukx_get_string(ctx, -2), dukx_get_string(ctx, -1)});
+
+        duk_pop_n(ctx, 2);
+    }
 
     return params;
 }
@@ -70,7 +74,7 @@
     std::string pattern = " \t\n";
 
     if (duk_is_string(ctx, 0))
-        result = string_util::split(dukx_get_std_string(ctx, 0), pattern);
+        result = string_util::split(dukx_get_string(ctx, 0), pattern);
     else if (duk_is_array(ctx, 0)) {
         duk_enum(ctx, 0, DUK_ENUM_ARRAY_INDICES_ONLY);
 
@@ -197,7 +201,12 @@
         return 1;
     }
 
-    dukx_push_array(ctx, list, dukx_push_std_string);
+    duk_push_array(ctx);
+
+    for (unsigned i = 0; i < list.size(); ++i) {
+        dukx_push_string(ctx, list[i]);
+        duk_put_prop_index(ctx, -2, i);
+    }
 
     return 1;
 }
@@ -217,9 +226,9 @@
 duk_ret_t format(duk_context* ctx)
 {
     try {
-        dukx_push_std_string(ctx, string_util::format(dukx_get_std_string(ctx, 0), get_subst(ctx, 1)));
+        dukx_push_string(ctx, string_util::format(dukx_get_string(ctx, 0), get_subst(ctx, 1)));
     } catch (const std::exception &ex) {
-        dukx_throw(ctx, SyntaxError(ex.what()));
+        (void)duk_error(ctx, DUK_ERR_SYNTAX_ERROR, "%s", ex.what());
     }
 
     return 1;
@@ -238,7 +247,7 @@
  */
 duk_ret_t splituser(duk_context* ctx)
 {
-    dukx_push_std_string(ctx, irc::user::parse(duk_require_string(ctx, 0)).nick());
+    dukx_push_string(ctx, irc::user::parse(duk_require_string(ctx, 0)).nick());
 
     return 1;
 }
@@ -256,7 +265,7 @@
  */
 duk_ret_t splithost(duk_context* ctx)
 {
-    dukx_push_std_string(ctx, irc::user::parse(duk_require_string(ctx, 0)).host());
+    dukx_push_string(ctx, irc::user::parse(duk_require_string(ctx, 0)).host());
 
     return 1;
 }
@@ -278,7 +287,7 @@
 
 void util_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin)
 {
-    StackAssert sa(plugin->context());
+    dukx_stack_assert sa(plugin->context());
 
     duk_get_global_string(plugin->context(), "Irccd");
     duk_push_object(plugin->context());
--- a/tests/CMakeLists.txt	Mon Nov 27 08:51:47 2017 +0100
+++ b/tests/CMakeLists.txt	Mon Nov 27 10:16:45 2017 +0100
@@ -61,7 +61,6 @@
         # Javascript plugin object.
         add_subdirectory(js-plugin)
 
-        add_subdirectory(js)
         add_subdirectory(js-elapsedtimer)
         add_subdirectory(js-directory)
         add_subdirectory(js-file)
--- a/tests/js-directory/main.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/tests/js-directory/main.cpp	Mon Nov 27 10:16:45 2017 +0100
@@ -36,7 +36,7 @@
     );
 
     if (duk_peval_string(plugin_->context(), script.c_str()) != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     duk_get_global_string(plugin_->context(), "l");
     BOOST_TEST(duk_get_int(plugin_->context(), -1) == 3);
--- a/tests/js-elapsedtimer/main.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/tests/js-elapsedtimer/main.cpp	Mon Nov 27 10:16:45 2017 +0100
@@ -34,12 +34,12 @@
 BOOST_AUTO_TEST_CASE(standard)
 {
     if (duk_peval_string(plugin_->context(), "timer = new Irccd.ElapsedTimer();") != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     std::this_thread::sleep_for(300ms);
 
     if (duk_peval_string(plugin_->context(), "result = timer.elapsed();") != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_REQUIRE(duk_get_global_string(plugin_->context(), "result"));
     BOOST_REQUIRE_GE(duk_get_int(plugin_->context(), -1), 250);
--- a/tests/js-file/main.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/tests/js-file/main.cpp	Mon Nov 27 10:16:45 2017 +0100
@@ -27,12 +27,30 @@
 
 namespace irccd {
 
+namespace {
+
+std::vector<std::string> array(duk_context* ctx, int index)
+{
+    std::vector<std::string> result;
+    std::size_t length = duk_get_length(ctx, index);
+
+    for (std::size_t i = 0U; i < length; ++i) {
+        duk_get_prop_index(ctx, -1, i);
+        result.push_back(dukx_get_string(ctx, -1));
+        duk_pop(ctx);
+    }
+
+    return result;
+}
+
+} // !namespace
+
 BOOST_FIXTURE_TEST_SUITE(file_jsapi_suite, js_test<file_jsapi>)
 
 BOOST_AUTO_TEST_CASE(function_basename)
 {
     if (duk_peval_string(plugin_->context(), "result = Irccd.File.basename('/usr/local/etc/irccd.conf');"))
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST("irccd.conf" == duk_get_string(plugin_->context(), -1));
@@ -41,7 +59,7 @@
 BOOST_AUTO_TEST_CASE(function_dirname)
 {
     if (duk_peval_string(plugin_->context(), "result = Irccd.File.dirname('/usr/local/etc/irccd.conf');"))
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST("/usr/local/etc" == duk_get_string(plugin_->context(), -1));
@@ -51,7 +69,7 @@
 BOOST_AUTO_TEST_CASE(function_exists)
 {
     if (duk_peval_string(plugin_->context(), "result = Irccd.File.exists(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt')"))
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
@@ -60,7 +78,7 @@
 BOOST_AUTO_TEST_CASE(function_exists2)
 {
     if (duk_peval_string(plugin_->context(), "result = Irccd.File.exists('file_which_does_not_exist.txt')"))
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST(!duk_get_boolean(plugin_->context(), -1));
@@ -72,7 +90,7 @@
     std::ofstream("test-js-fs.remove");
 
     if (duk_peval_string(plugin_->context(), "Irccd.File.remove('test-js-fs.remove');") != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     std::ifstream in("test-js-fs.remove");
 
@@ -87,7 +105,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST("file-1.txt" == duk_get_string(plugin_->context(), -1));
@@ -102,7 +120,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST("file-1.txt" == duk_get_string(plugin_->context(), -1));
@@ -116,7 +134,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST(CMAKE_SOURCE_DIR "/tests/root" == duk_get_string(plugin_->context(), -1));
@@ -131,7 +149,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST(CMAKE_SOURCE_DIR "/tests/root" == duk_get_string(plugin_->context(), -1));
@@ -144,12 +162,12 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     std::vector<std::string> expected{"a", "b", "c"};
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(expected == dukx_get_array(plugin_->context(), -1, dukx_get_std_string));
+    BOOST_TEST(expected == array(plugin_->context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_seek1)
@@ -161,10 +179,10 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(".", dukx_get_std_string(plugin_->context(), -1));
+    BOOST_TEST(".", dukx_get_string(plugin_->context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_seek1_closed)
@@ -178,7 +196,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
@@ -194,10 +212,10 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST("." == dukx_get_std_string(plugin_->context(), -1));
+    BOOST_TEST("." == dukx_get_string(plugin_->context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_seek2c_losed)
@@ -212,7 +230,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
@@ -227,7 +245,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST("t" == duk_get_string(plugin_->context(), -1));
@@ -244,7 +262,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
@@ -258,7 +276,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST("file-1.txt\n" == duk_get_string(plugin_->context(), -1));
@@ -275,12 +293,12 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     std::vector<std::string> expected{"a", "b", "c"};
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(expected == dukx_get_array(plugin_->context(), -1, dukx_get_std_string));
+    BOOST_TEST(expected == array(plugin_->context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_readline_closed)
@@ -295,12 +313,12 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     std::vector<std::string> expected;
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(expected == dukx_get_array(plugin_->context(), -1, dukx_get_std_string));
+    BOOST_TEST(expected == array(plugin_->context(), -1));
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/js-irccd/main.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/tests/js-irccd/main.cpp	Mon Nov 27 10:16:45 2017 +0100
@@ -34,7 +34,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "major"));
     BOOST_TEST(IRCCD_VERSION_MAJOR == duk_get_int(plugin_->context(), -1));
@@ -59,7 +59,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "errno"));
     BOOST_TEST(1 == duk_get_int(plugin_->context(), -1));
@@ -96,7 +96,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "errno"));
     BOOST_TEST(EINVAL == duk_get_int(plugin_->context(), -1));
--- a/tests/js-logger/main.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/tests/js-logger/main.cpp	Mon Nov 27 10:16:45 2017 +0100
@@ -72,7 +72,7 @@
 BOOST_AUTO_TEST_CASE(info)
 {
     if (duk_peval_string(plugin_->context(), "Irccd.Logger.info(\"hello!\");") != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST("plugin test: hello!" == line_info);
 }
@@ -80,7 +80,7 @@
 BOOST_AUTO_TEST_CASE(warning)
 {
     if (duk_peval_string(plugin_->context(), "Irccd.Logger.warning(\"FAIL!\");") != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST("plugin test: FAIL!" == line_warning);
 }
@@ -90,7 +90,7 @@
 BOOST_AUTO_TEST_CASE(debug)
 {
     if (duk_peval_string(plugin_->context(), "Irccd.Logger.debug(\"starting\");") != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST("plugin test: starting" == line_debug);
 }
--- a/tests/js-system/main.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/tests/js-system/main.cpp	Mon Nov 27 10:16:45 2017 +0100
@@ -51,7 +51,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "r"));
     BOOST_TEST(duk_get_string(plugin_->context(), -1) == IRCCD_VERSION);
--- a/tests/js-util/main.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ b/tests/js-util/main.cpp	Mon Nov 27 10:16:45 2017 +0100
@@ -39,7 +39,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST(duk_get_string(plugin_->context(), -1) == "markand");
@@ -48,7 +48,7 @@
 BOOST_AUTO_TEST_CASE(splituser)
 {
     if (duk_peval_string(plugin_->context(), "result = Irccd.Util.splituser(\"user!~user@hyper/super/host\");") != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST(duk_get_string(plugin_->context(), -1) == "user");
@@ -57,7 +57,7 @@
 BOOST_AUTO_TEST_CASE(splithost)
 {
     if (duk_peval_string(plugin_->context(), "result = Irccd.Util.splithost(\"user!~user@hyper/super/host\");") != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST(duk_get_string(plugin_->context(), -1) == "~user@hyper/super/host");
@@ -76,7 +76,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "line0"));
     BOOST_TEST(duk_get_string(plugin_->context(), -1) == "hello world");
@@ -91,7 +91,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "line0"));
     BOOST_TEST(duk_get_string(plugin_->context(), -1) == "hello");
@@ -108,7 +108,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "line0"));
     BOOST_TEST(duk_get_string(plugin_->context(), -1) == "hello");
@@ -123,7 +123,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "lines"));
     BOOST_TEST(duk_is_undefined(plugin_->context(), -1));
@@ -141,7 +141,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "name"));
     BOOST_TEST(duk_get_string(plugin_->context(), -1) == "RangeError");
@@ -161,7 +161,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "name"));
     BOOST_TEST(duk_get_string(plugin_->context(), -1) == "RangeError");
@@ -181,7 +181,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "name"));
     BOOST_TEST(duk_get_string(plugin_->context(), -1) == "RangeError");
@@ -197,7 +197,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "line0"));
     BOOST_TEST(duk_get_string(plugin_->context(), -1) == "hello world");
@@ -212,7 +212,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "line0"));
     BOOST_TEST(duk_get_string(plugin_->context(), -1) == "hello");
@@ -229,7 +229,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "line0"));
     BOOST_TEST(duk_get_string(plugin_->context(), -1) == "hello");
@@ -249,7 +249,7 @@
     );
 
     if (ret != 0)
-        throw dukx_exception(plugin_->context(), -1);
+        throw dukx_get_exception(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "name"));
     BOOST_TEST(duk_get_string(plugin_->context(), -1) == "TypeError");
--- a/tests/js/CMakeLists.txt	Mon Nov 27 08:51:47 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-#
-# CMakeLists.txt -- CMake build system for irccd
-#
-# Copyright (c) 2013-2017 David Demelier <markand@malikania.fr>
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-#
-
-irccd_define_test(
-    NAME js
-    SOURCES main.cpp
-    LIBRARIES libirccd-js
-)
-
--- a/tests/js/main.cpp	Mon Nov 27 08:51:47 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/*
- * main.cpp -- test duktape.hpp extensions
- *
- * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#define BOOST_TEST_MODULE "Javascript"
-#include <boost/test/unit_test.hpp>
-
-#include <js/duktape.hpp>
-
-#include <irccd/fs_util.hpp>
-#include <irccd/util.hpp>
-
-namespace irccd {
-
-class test {
-protected:
-    UniqueContext ctx_;
-};
-
-/*
- * dukx_peval_file
- * ------------------------------------------------------------------
- */
-
-BOOST_FIXTURE_TEST_SUITE(test_suite, test)
-
-BOOST_AUTO_TEST_CASE(no_file)
-{
-    BOOST_REQUIRE_THROW(dukx_peval_file(ctx_, "nonexistent"), Exception);
-
-    try {
-        dukx_peval_file(ctx_, "nonexistent");
-    } catch (const Exception& ex) {
-        BOOST_REQUIRE_EQUAL("Error", ex.name);
-        BOOST_REQUIRE_EQUAL("nonexistent", ex.fileName);
-    }
-}
-
-BOOST_AUTO_TEST_CASE(syntax_error)
-{
-    BOOST_REQUIRE_THROW(dukx_peval_file(ctx_, CMAKE_CURRENT_SOURCE_DIR "/syntax-error.js"), Exception);
-
-    try {
-        dukx_peval_file(ctx_, CMAKE_CURRENT_SOURCE_DIR "/syntax-error.js");
-    } catch (const Exception& ex) {
-        BOOST_REQUIRE_EQUAL("SyntaxError", ex.name);
-        BOOST_REQUIRE_EQUAL("syntax-error.js", fs_util::base_name(ex.fileName));
-        BOOST_REQUIRE_EQUAL(6, ex.lineNumber);
-        BOOST_REQUIRE_EQUAL("empty expression not allowed (line 6)", ex.message);
-    }
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
-} // !irccd
--- a/tests/js/syntax-error.js	Mon Nov 27 08:51:47 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-function x()
-{
-}
-
-var y = 123;
-y+++;
-
-function z()
-{
-}