changeset 566:bf56628e070b

Irccd: import new Duktape helpers
author David Demelier <markand@malikania.fr>
date Mon, 27 Nov 2017 15:03:38 +0100
parents 194162717ec9
children cf734c969727
files libirccd-js/irccd/js/directory_jsapi.cpp libirccd-js/irccd/js/duktape.hpp libirccd-js/irccd/js/file_jsapi.cpp libirccd-js/irccd/js/irccd_jsapi.cpp libirccd-js/irccd/js/irccd_jsapi.hpp libirccd-js/irccd/js/js_plugin.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/util_jsapi.cpp 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
diffstat 18 files changed, 876 insertions(+), 318 deletions(-) [+]
line wrap: on
line diff
--- a/libirccd-js/irccd/js/directory_jsapi.cpp	Mon Nov 27 10:16:45 2017 +0100
+++ b/libirccd-js/irccd/js/directory_jsapi.cpp	Mon Nov 27 15:03:38 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_string(ctx, -1);
+    auto ret = dukx_get<std::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_string(ctx, pattern_index), recursive);
+            path = fs_util::find(base, dukx_get<std::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_string(ctx, path);
+        dukx_push(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_string(ctx, entry.path().filename().string());
+            dukx_push(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");
@@ -209,8 +209,8 @@
         duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE);
 
         // 'path' property.
-        duk_push_string(ctx, "path");
-        dukx_push_string(ctx, path);
+        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()));
--- a/libirccd-js/irccd/js/duktape.hpp	Mon Nov 27 10:16:45 2017 +0100
+++ b/libirccd-js/irccd/js/duktape.hpp	Mon Nov 27 15:03:38 2017 +0100
@@ -1,7 +1,7 @@
 /*
- * duktape.hpp -- Duktape extras
+ * duktape.hpp -- miscellaneous Duktape extras
  *
- * Copyright (c) 2016-2017 David Demelier <markand@malikania.fr>
+ * 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
@@ -16,24 +16,26 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#ifndef IRCCD_JS_DUKTAPE_HPP
-#define IRCCD_JS_DUKTAPE_HPP
+#ifndef DUKTAPE_HPP
+#define DUKTAPE_HPP
 
 /**
  * \file duktape.hpp
- * \brief Bring some extras to Duktape C library.
+ * \brief Miscellaneous Duktape extras
  * \author David Demelier <markand@malikania.fr>
+ * \version 0.1.0
  */
 
+#include <cassert>
 #include <cstdio>
-#include <cstdlib>
+#include <initializer_list>
+#include <iterator>
 #include <memory>
 #include <string>
-#include <unordered_map>
+#include <type_traits>
 #include <utility>
-#include <vector>
 
-#include <duktape.h>
+#include "duktape.h"
 
 namespace irccd {
 
@@ -102,37 +104,13 @@
 };
 
 /**
- * \brief Error description.
- *
- * This class fills the fields got in an Error object.
- */
-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 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
-    {
-        return message.c_str();
-    }
-};
-
-/**
  * \brief RAII based Duktape handler.
  *
  * This class is implicitly convertible to duk_context for convenience.
  */
 class dukx_context {
 private:
-    std::unique_ptr<duk_context, void (*)(duk_context*)> m_handle;
+    std::unique_ptr<duk_context, void (*)(duk_context*)> handle_;
 
     dukx_context(const dukx_context&) = delete;
     dukx_context &operator=(const dukx_context&) = delete;
@@ -142,7 +120,7 @@
      * Create default context.
      */
     inline dukx_context() noexcept
-        : m_handle(duk_create_heap_default(), duk_destroy_heap)
+        : handle_(duk_create_heap_default(), duk_destroy_heap)
     {
     }
 
@@ -158,7 +136,7 @@
      */
     inline operator duk_context*() noexcept
     {
-        return m_handle.get();
+        return handle_.get();
     }
 
     /**
@@ -168,7 +146,7 @@
      */
     inline operator duk_context*() const noexcept
     {
-        return m_handle.get();
+        return handle_.get();
     }
 
     /**
@@ -180,6 +158,692 @@
 };
 
 /**
+ * \brief Error description.
+ *
+ * This class fills the fields got in an Error object.
+ */
+class dukx_stack_info : public std::exception {
+private:
+    std::string name_;
+    std::string message_;
+    std::string stack_;
+    std::string file_name_;
+    int line_number_;
+
+public:
+    /**
+     * Construct the stack information.
+     *
+     * \param name the exception name (e.g. ReferenceError)
+     * \param message the error message
+     * \param stack the stack trace
+     * \param file_name the optional filename
+     * \param line_number the optional line number
+     */
+    inline dukx_stack_info(std::string name,
+                           std::string message,
+                           std::string stack,
+                           std::string file_name,
+                           int line_number = 0) noexcept
+        : name_(std::move(name))
+        , message_(std::move(message))
+        , stack_(std::move(stack))
+        , file_name_(std::move(file_name))
+        , line_number_(line_number)
+    {
+    }
+
+    /**
+     * Get the exception name.
+     *
+     * \return the exception name (e.g. ReferenceError)
+     */
+    inline const std::string& name() const noexcept
+    {
+        return name_;
+    }
+
+    /**
+     * Get the error message.
+     *
+     * \return the message
+     */
+    inline const std::string& message() const noexcept
+    {
+        return message_;
+    }
+
+    /**
+     * Get the stack trace.
+     *
+     * \return the stack
+     */
+    inline const std::string& stack() const noexcept
+    {
+        return stack_;
+    }
+
+    /**
+     * Get the optional file name.
+     *
+     * \return the file name
+     */
+    inline const std::string& file_name() const noexcept
+    {
+        return file_name_;
+    }
+
+    /**
+     * Get the line number.
+     *
+     * \return the line number
+     */
+    inline int line_number() const noexcept
+    {
+        return line_number_;
+    }
+
+    /**
+     * Get the error message. This effectively returns message field.
+     *
+     * \return the message
+     */
+    const char* what() const noexcept override
+    {
+        return message_.c_str();
+    }
+};
+
+/**
+ * \brief Operations on different types.
+ *
+ * This class provides some functions for the given type, depending on the
+ * nature of the function.
+ *
+ * For example, dukx_push will call dukx_type_traits<T>::push static function
+ * if the dukx_type_traits is implemented for that given T type.
+ *
+ * This helps passing/getting function between Javascript and C++ code.
+ *
+ * Example:
+ *
+ * ```cpp
+ * dukx_push(ctx, 123);     // Uses dukx_type_traits<int>
+ * dukx_push(ctx, true);    // Uses dukx_type_traits<bool>
+ * ```
+ *
+ * This class is specialized for the following types:
+ *
+ *   - `bool`,
+ *   - `duk_int_t`,
+ *   - `duk_uint_t`,
+ *   - `duk_double_t`,
+ *   - `const char*`,
+ *   - `std::string`
+ */
+template <typename T>
+class dukx_type_traits : public std::false_type {
+};
+
+/**
+ * \brief Specialization for bool.
+ */
+template <>
+class dukx_type_traits<bool> : public std::true_type {
+public:
+    /**
+     * Push a boolean.
+     *
+     * Uses duk_push_boolean
+     *
+     * \param ctx the Duktape context
+     * \param value the value
+     */
+    static void push(duk_context* ctx, bool value)
+    {
+        duk_push_boolean(ctx, value);
+    }
+
+    /**
+     * Get a boolean.
+     *
+     * Uses duk_get_boolean.
+     *
+     * \param ctx the Duktape context
+     * \param index the value index
+     * \return the converted value
+     */
+    static bool get(duk_context* ctx, duk_idx_t index)
+    {
+        return duk_get_boolean(ctx, index);
+    }
+};
+
+/**
+ * \brief Specialization for duk_double_t.
+ */
+template <>
+class dukx_type_traits<duk_double_t> : public std::true_type {
+public:
+    /**
+     * Push a double.
+     *
+     * Uses duk_push_number
+     *
+     * \param ctx the Duktape context
+     * \param value the value
+     */
+    static void push(duk_context* ctx, duk_double_t value)
+    {
+        duk_push_number(ctx, value);
+    }
+
+    /**
+     * Get a double.
+     *
+     * Uses duk_get_number.
+     *
+     * \param ctx the Duktape context
+     * \param index the value index
+     * \return the converted value
+     */
+    static duk_double_t get(duk_context* ctx, duk_idx_t index)
+    {
+        return duk_get_number(ctx, index);
+    }
+};
+
+/**
+ * \brief Specialization for duk_int_t.
+ */
+template <>
+class dukx_type_traits<duk_int_t> : public std::true_type {
+public:
+    /**
+     * Push an int.
+     *
+     * Uses duk_push_int
+     *
+     * \param ctx the Duktape context
+     * \param value the value
+     */
+    static void push(duk_context* ctx, duk_int_t value)
+    {
+        duk_push_int(ctx, value);
+    }
+
+    /**
+     * Get an int.
+     *
+     * Uses duk_get_number.
+     *
+     * \param ctx the Duktape context
+     * \param index the value index
+     * \return the converted value
+     */
+    static duk_int_t get(duk_context* ctx, duk_idx_t index)
+    {
+        return duk_get_int(ctx, index);
+    }
+};
+
+/**
+ * \brief Specialization for duk_uint_t.
+ */
+template <>
+class dukx_type_traits<duk_uint_t> : public std::true_type {
+public:
+    /**
+     * Push an unsigned int.
+     *
+     * Uses duk_push_uint
+     *
+     * \param ctx the Duktape context
+     * \param value the value
+     */
+    static void push(duk_context* ctx, duk_uint_t value)
+    {
+        duk_push_uint(ctx, value);
+    }
+
+    /**
+     * Get an unsigned int.
+     *
+     * Uses duk_get_uint.
+     *
+     * \param ctx the Duktape context
+     * \param index the value index
+     * \return the converted value
+     */
+    static duk_uint_t get(duk_context* ctx, duk_idx_t index)
+    {
+        return duk_get_uint(ctx, index);
+    }
+};
+
+/**
+ * \brief Specialization for C strings.
+ */
+template <>
+class dukx_type_traits<const char*> : public std::true_type {
+public:
+    /**
+     * Push a C string.
+     *
+     * Uses duk_push_string
+     *
+     * \param ctx the Duktape context
+     * \param value the value
+     */
+    static void push(duk_context* ctx, const char* value)
+    {
+        duk_push_string(ctx, value);
+    }
+
+    /**
+     * Get a C string.
+     *
+     * Uses duk_get_string.
+     *
+     * \param ctx the Duktape context
+     * \param index the value index
+     * \return the converted value
+     */
+    static const char* get(duk_context* ctx, duk_idx_t index)
+    {
+        return duk_get_string(ctx, index);
+    }
+};
+
+/**
+ * \brief Specialization for C++ std::strings.
+ */
+template <>
+class dukx_type_traits<std::string> : public std::true_type {
+public:
+    /**
+     * Push a C++ std::string.
+     *
+     * Uses duk_push_lstring
+     *
+     * \param ctx the Duktape context
+     * \param value the value
+     */
+    static void push(duk_context* ctx, const std::string& value)
+    {
+        duk_push_lstring(ctx, value.data(), value.size());
+    }
+
+    /**
+     * Get a C++ std::string.
+     *
+     * Uses duk_get_lstring.
+     *
+     * \param ctx the Duktape context
+     * \param index the value index
+     * \return the converted value
+     */
+    static std::string get(duk_context* ctx, duk_idx_t index)
+    {
+        duk_size_t length;
+        const char* str = duk_get_lstring(ctx, index, &length);
+
+        return {str, length};
+    }
+};
+
+/**
+ * Generic push function.
+ *
+ * This function calls dukx_type_traits<T>::push if specialized.
+ *
+ * \param ctx the Duktape context
+ * \param value the forwarded value
+ * \return 1 for convenience
+ */
+template <typename T>
+int dukx_push(duk_context* ctx, T&& value)
+{
+    using Type = typename std::decay<T>::type;
+
+    static_assert(dukx_type_traits<Type>::value, "type T not supported");
+
+    dukx_type_traits<Type>::push(ctx, std::forward<T>(value));
+
+    return 1;
+}
+
+/**
+ * Generic get function.
+ *
+ * This functions calls dukx_type_traits<T>::get if specialized.
+ *
+ * \param ctx the Duktape context
+ * \param index the value index
+ * \return the converted value
+ */
+template <typename T>
+T dukx_get(duk_context* ctx, duk_idx_t index)
+{
+    using Type = typename std::decay<T>::type;
+
+    static_assert(dukx_type_traits<Type>::value, "type T not supported");
+
+    return dukx_type_traits<Type>::get(ctx, index);
+}
+
+/**
+ * Push a Javascript array to the stack.
+ *
+ * This function push the value using duk_push_array and dukx_push for each
+ * value between the range
+ *
+ * \param ctx the Duktape context
+ * \param it the input iterator
+ * \param end the end iterator
+ * \return 1 for convenience
+ */
+template <typename InputIt>
+int dukx_push_array(duk_context* ctx, InputIt it, InputIt end)
+{
+    duk_push_array(ctx);
+
+    for (int i = 0; it != end; ++it) {
+        dukx_push(ctx, *it);
+        duk_put_prop_index(ctx, -2, i++);
+    }
+
+    return 1;
+}
+
+/**
+ * Overloaded function.
+ *
+ * Alias for `dukx_push_array(ctx, values.begin(), values.end());`
+ *
+ * \param ctx the Duktape context
+ * \param values the list of values
+ * \return 1 for convenience
+ */
+template <typename T>
+int dukx_push_array(duk_context* ctx, std::initializer_list<T> values)
+{
+    return dukx_push_array(ctx, values.begin(), values.end());
+}
+
+/**
+ * Push a Javascript object to the stack.
+ *
+ * Fhis function push the value using duk_push_object and dukx_push for each
+ * key/value pair combination in InputIt.
+ *
+ * The input iterator must have first and second values (usually `std::pair`).
+ *
+ * \param ctx the Duktape context
+ * \param it the input iterator
+ * \param end the end iterator
+ * \return 1 for convenience
+ */
+template <typename InputIt>
+int dukx_push_object(duk_context* ctx, InputIt it, InputIt end)
+{
+    duk_push_object(ctx);
+
+    while (it != end) {
+        dukx_push(ctx, it->first);
+        dukx_push(ctx, it++->second);
+        duk_put_prop(ctx, -3);
+    }
+
+    return 1;
+}
+
+/**
+ * Overloaded function.
+ *
+ * Alias for `dukx_push_object(ctx, values.begin(), values.end());`
+ *
+ * \param ctx the Duktape context
+ * \param values the list of key/value values
+ * \return 1 for convenience
+ */
+template <typename T>
+int dukx_push_object(duk_context* ctx, std::initializer_list<std::pair<std::string, T>> values)
+{
+    return dukx_push_object(ctx, values.begin(), values.end());
+}
+
+/**
+ * Get values from a Javascript array.
+ *
+ * This function uses dukx_get<T> for each value in the Javascript array and
+ * append them to the output iterator.
+ *
+ * \param ctx the Duktape context
+ * \param index the array index
+ * \param output the output iterator
+ */
+template <typename T, typename OutputIt>
+void dukx_get_array(duk_context* ctx, duk_idx_t index, OutputIt output)
+{
+    std::size_t length = duk_get_length(ctx, index);
+
+    for (std::size_t i = 0; i < length; ++i) {
+        duk_get_prop_index(ctx, index, i);
+        *output++ = dukx_get<T>(ctx, -1);
+        duk_pop(ctx);
+    }
+}
+
+/**
+ * Overloaded function.
+ *
+ * Construct a container and return it.
+ *
+ * \param ctx the Duktape context
+ * \param index the array index
+ * \return the container of values (e.g. `std::vector<int>`)
+ */
+template <typename Container>
+Container dukx_get_array(duk_context* ctx, duk_idx_t index)
+{
+    using T = typename Container::value_type;
+
+    Container result;
+
+    dukx_get_array<T>(ctx, index, std::back_inserter(result));
+
+    return result;
+}
+
+/**
+ * Get values from a Javascript object.
+ *
+ * \param ctx the Duktape context
+ * \param index the object index
+ * \return the container of values (e.g. `std::map<std::string, int>`)
+ */
+template <typename Container>
+Container dukx_get_object(duk_context* ctx, duk_idx_t index)
+{
+    using T = typename Container::mapped_type;
+
+    Container result;
+
+    duk_enum(ctx, index, 0);
+
+    while (duk_next(ctx, -1, true)) {
+        result.emplace(dukx_get<std::string>(ctx, -2), dukx_get<T>(ctx, -1));
+        duk_pop_n(ctx, 2);
+    }
+
+    duk_pop(ctx);
+
+    return result;
+}
+
+/**
+ * \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.
+ *
+ * \param ctx the Duktape context
+ * \param error the error object
+ */
+template <typename Error>
+void dukx_throw(duk_context* ctx, const Error& error)
+{
+    error.create(ctx);
+
+    (void)duk_throw(ctx);
+}
+
+/**
  * Get the error object when a JavaScript error has been thrown (e.g. eval
  * failure).
  *
@@ -188,83 +852,34 @@
  * \param pop if true, also remove the exception from the stack
  * \return the information
  */
-inline dukx_exception dukx_get_exception(duk_context* ctx, int index, bool pop = true)
+inline dukx_stack_info dukx_stack(duk_context* ctx, int index, bool pop = true)
 {
-    dukx_exception ex;
-
     index = duk_normalize_index(ctx, index);
 
     duk_get_prop_string(ctx, index, "name");
-    ex.name = duk_to_string(ctx, -1);
+    auto name = duk_to_string(ctx, -1);
     duk_get_prop_string(ctx, index, "message");
-    ex.message = duk_to_string(ctx, -1);
+    auto message = duk_to_string(ctx, -1);
     duk_get_prop_string(ctx, index, "fileName");
-    ex.file_name = duk_to_string(ctx, -1);
+    auto file_name = duk_to_string(ctx, -1);
     duk_get_prop_string(ctx, index, "lineNumber");
-    ex.line_number = duk_to_int(ctx, -1);
+    auto line_number = duk_to_int(ctx, -1);
     duk_get_prop_string(ctx, index, "stack");
-    ex.stack = duk_to_string(ctx, -1);
+    auto stack = duk_to_string(ctx, -1);
     duk_pop_n(ctx, 5);
 
     if (pop)
         duk_remove(ctx, index);
 
-    return ex;
-}
-
-/**
- * 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 index
- * \return the string
- */
-inline std::string dukx_require_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_string(duk_context* ctx, const std::string& str)
-{
-    duk_push_lstring(ctx, str.data(), str.length());
-}
-
-/**
- * Throw an ECMAScript exception.
- *
- * \param ctx the context
- * \param ex the exception
- */
-template <typename Exception>
-void dukx_throw(duk_context *ctx, const Exception &ex)
-{
-    ex.raise(ctx);
+    return {
+        std::move(name),
+        std::move(message),
+        std::move(stack),
+        std::move(file_name),
+        line_number
+    };
 }
 
 } // !irccd
 
-#endif // !MALIKANIA_COMMON_DUKTAPE_HPP
+#endif // !DUKTAPE_HPP
--- a/libirccd-js/irccd/js/file_jsapi.cpp	Mon Nov 27 10:16:45 2017 +0100
+++ b/libirccd-js/irccd/js/file_jsapi.cpp	Mon Nov 27 15:03:38 2017 +0100
@@ -153,9 +153,7 @@
  */
 duk_ret_t method_basename(duk_context* ctx)
 {
-    dukx_push_string(ctx, fs_util::base_name(self(ctx)->path()));
-
-    return 1;
+    return dukx_push(ctx, fs_util::base_name(self(ctx)->path()));
 }
 
 /*
@@ -182,9 +180,7 @@
  */
 duk_ret_t method_dirname(duk_context* ctx)
 {
-    dukx_push_string(ctx, fs_util::dir_name(self(ctx)->path()));
-
-    return 1;
+    return dukx_push(ctx, fs_util::dir_name(self(ctx)->path()));
 }
 
 /*
@@ -213,7 +209,7 @@
         auto pos = buffer.find('\n');
 
         if (pos != std::string::npos) {
-            dukx_push_string(ctx, clear_crlf(buffer.substr(0, pos)));
+            dukx_push(ctx, clear_crlf(buffer.substr(0, pos)));
             duk_put_prop_index(ctx, -2, i++);
 
             buffer.erase(0, pos + 1);
@@ -226,7 +222,7 @@
 
     // Missing '\n' in end of file.
     if (!buffer.empty()) {
-        dukx_push_string(ctx, clear_crlf(buffer));
+        dukx_push(ctx, clear_crlf(buffer));
         duk_put_prop_index(ctx, -2, i++);
     }
 
@@ -279,7 +275,7 @@
             data.resize(total);
         }
 
-        dukx_push_string(ctx, data);
+        dukx_push(ctx, data);
     } catch (const std::exception&) {
         dukx_throw(ctx, system_error());
     }
@@ -310,9 +306,7 @@
     if (std::ferror(fp))
         dukx_throw(ctx, system_error());
 
-    dukx_push_string(ctx, clear_crlf(result));
-
-    return 1;
+    return dukx_push(ctx, clear_crlf(result));
 }
 
 /*
@@ -519,9 +513,7 @@
  */
 duk_ret_t function_basename(duk_context* ctx)
 {
-    dukx_push_string(ctx, fs_util::base_name(duk_require_string(ctx, 0)));
-
-    return 1;
+    return dukx_push(ctx, fs_util::base_name(duk_require_string(ctx, 0)));
 }
 
 /*
@@ -537,9 +529,7 @@
  */
 duk_ret_t function_dirname(duk_context* ctx)
 {
-    dukx_push_string(ctx, fs_util::dir_name(duk_require_string(ctx, 0)));
-
-    return 1;
+    return dukx_push(ctx, fs_util::dir_name(duk_require_string(ctx, 0)));
 }
 
 /*
--- a/libirccd-js/irccd/js/irccd_jsapi.cpp	Mon Nov 27 10:16:45 2017 +0100
+++ b/libirccd-js/irccd/js/irccd_jsapi.cpp	Mon Nov 27 15:03:38 2017 +0100
@@ -158,17 +158,16 @@
 {
 }
 
-void system_error::raise(duk_context *ctx) const
+void system_error::create(duk_context *ctx) const
 {
-    dukx_stack_assert sa(ctx, 0);
+    dukx_stack_assert sa(ctx, 1);
 
     duk_get_global_string(ctx, "Irccd");
     duk_get_prop_string(ctx, -1, "SystemError");
     duk_remove(ctx, -2);
     duk_push_int(ctx, errno_);
-    dukx_push_string(ctx, message_);
+    dukx_push(ctx, message_);
     duk_new(ctx, 2);
-    duk_throw(ctx);
 }
 
 std::string irccd_jsapi::name() const
--- a/libirccd-js/irccd/js/irccd_jsapi.hpp	Mon Nov 27 10:16:45 2017 +0100
+++ b/libirccd-js/irccd/js/irccd_jsapi.hpp	Mon Nov 27 15:03:38 2017 +0100
@@ -55,11 +55,11 @@
     system_error(int e, std::string message);
 
     /**
-     * Raise the SystemError Javascript exception.
+     * Create the SystemError Javascript exception.
      *
      * \param ctx the context
      */
-    void raise(duk_context* ctx) const;
+    void create(duk_context* ctx) const;
 };
 
 /**
--- a/libirccd-js/irccd/js/js_plugin.cpp	Mon Nov 27 10:16:45 2017 +0100
+++ b/libirccd-js/irccd/js/js_plugin.cpp	Mon Nov 27 15:03:38 2017 +0100
@@ -58,7 +58,7 @@
     duk_get_global_string(context_, name.c_str());
 
     for (const auto& pair : vars) {
-        dukx_push_string(context_, pair.second);
+        dukx_push(context_, pair.second);
         duk_put_prop_string(context_, -2, pair.first.c_str());
     }
 
@@ -77,7 +77,7 @@
         duk_insert(context_, -nargs - 1);
 
         if (duk_pcall(context_, nargs) != 0)
-            throw dukx_get_exception(context_, -1, true);
+            throw dukx_stack(context_, -1, true);
 
         duk_pop(context_);
     }
@@ -107,9 +107,9 @@
 
     duk_push_pointer(context_, this);
     duk_put_global_string(context_, "\xff""\xff""plugin");
-    dukx_push_string(context_, name);
+    dukx_push(context_, name);
     duk_put_global_string(context_, "\xff""\xff""name");
-    dukx_push_string(context_, path);
+    dukx_push(context_, path);
     duk_put_global_string(context_, "\xff""\xff""path");
 }
 
@@ -126,7 +126,7 @@
     );
 
     if (duk_peval_string(context_, data.c_str()))
-        throw dukx_get_exception(context_, -1);
+        throw dukx_stack(context_, -1);
 
     // Read metadata.
     duk_get_global_string(context_, "info");
@@ -151,10 +151,10 @@
     dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_string(context_, event.origin);
-    dukx_push_string(context_, event.channel);
-    dukx_push_string(context_, event.mode);
-    dukx_push_string(context_, event.argument);
+    dukx_push(context_, event.origin);
+    dukx_push(context_, event.channel);
+    dukx_push(context_, event.mode);
+    dukx_push(context_, event.argument);
     call("onChannelMode", 5);
 }
 
@@ -163,9 +163,9 @@
     dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_string(context_, event.origin);
-    dukx_push_string(context_, event.channel);
-    dukx_push_string(context_, event.message);
+    dukx_push(context_, event.origin);
+    dukx_push(context_, event.channel);
+    dukx_push(context_, event.message);
     call("onChannelNotice", 4);
 }
 
@@ -174,9 +174,9 @@
     dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_string(context_, event.origin);
-    dukx_push_string(context_, event.channel);
-    dukx_push_string(context_, event.message);
+    dukx_push(context_, event.origin);
+    dukx_push(context_, event.channel);
+    dukx_push(context_, event.message);
     call("onCommand", 4);
 }
 
@@ -193,8 +193,8 @@
     dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_string(context_, event.origin);
-    dukx_push_string(context_, event.channel);
+    dukx_push(context_, event.origin);
+    dukx_push(context_, event.channel);
     call("onInvite", 3);
 }
 
@@ -203,8 +203,8 @@
     dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_string(context_, event.origin);
-    dukx_push_string(context_, event.channel);
+    dukx_push(context_, event.origin);
+    dukx_push(context_, event.channel);
     call("onJoin", 3);
 }
 
@@ -213,10 +213,10 @@
     dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_string(context_, event.origin);
-    dukx_push_string(context_, event.channel);
-    dukx_push_string(context_, event.target);
-    dukx_push_string(context_, event.reason);
+    dukx_push(context_, event.origin);
+    dukx_push(context_, event.channel);
+    dukx_push(context_, event.target);
+    dukx_push(context_, event.reason);
     call("onKick", 5);
 }
 
@@ -232,9 +232,9 @@
     dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_string(context_, event.origin);
-    dukx_push_string(context_, event.channel);
-    dukx_push_string(context_, event.message);
+    dukx_push(context_, event.origin);
+    dukx_push(context_, event.channel);
+    dukx_push(context_, event.message);
     call("onMessage", 4);
 }
 
@@ -243,9 +243,9 @@
     dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_string(context_, event.origin);
-    dukx_push_string(context_, event.channel);
-    dukx_push_string(context_, event.message);
+    dukx_push(context_, event.origin);
+    dukx_push(context_, event.channel);
+    dukx_push(context_, event.message);
     call("onMe", 4);
 }
 
@@ -254,8 +254,8 @@
     dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_string(context_, event.origin);
-    dukx_push_string(context_, event.mode);
+    dukx_push(context_, event.origin);
+    dukx_push(context_, event.mode);
     call("onMode", 3);
 }
 
@@ -264,13 +264,8 @@
     dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    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);
-    }
+    dukx_push(context_, event.channel);
+    dukx_push_array(context_, event.names.begin(), event.names.end());
 
     call("onNames", 3);
 }
@@ -280,8 +275,8 @@
     dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_string(context_, event.origin);
-    dukx_push_string(context_, event.nickname);
+    dukx_push(context_, event.origin);
+    dukx_push(context_, event.nickname);
     call("onNick", 3);
 }
 
@@ -290,8 +285,8 @@
     dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_string(context_, event.origin);
-    dukx_push_string(context_, event.message);
+    dukx_push(context_, event.origin);
+    dukx_push(context_, event.message);
     call("onNotice", 3);
 }
 
@@ -300,9 +295,9 @@
     dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_string(context_, event.origin);
-    dukx_push_string(context_, event.channel);
-    dukx_push_string(context_, event.reason);
+    dukx_push(context_, event.origin);
+    dukx_push(context_, event.channel);
+    dukx_push(context_, event.reason);
     call("onPart", 4);
 }
 
@@ -311,8 +306,8 @@
     dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_string(context_, event.origin);
-    dukx_push_string(context_, event.message);
+    dukx_push(context_, event.origin);
+    dukx_push(context_, event.message);
     call("onQuery", 3);
 }
 
@@ -321,8 +316,8 @@
     dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_string(context_, event.origin);
-    dukx_push_string(context_, event.message);
+    dukx_push(context_, event.origin);
+    dukx_push(context_, event.message);
     call("onQueryCommand", 3);
 }
 
@@ -338,9 +333,9 @@
     dukx_stack_assert sa(context_);
 
     dukx_push_server(context_, std::move(event.server));
-    dukx_push_string(context_, event.origin);
-    dukx_push_string(context_, event.channel);
-    dukx_push_string(context_, event.topic);
+    dukx_push(context_, event.origin);
+    dukx_push(context_, event.channel);
+    dukx_push(context_, event.topic);
     call("onTopic", 4);
 }
 
@@ -357,21 +352,15 @@
 
     dukx_push_server(context_, std::move(event.server));
     duk_push_object(context_);
-    dukx_push_string(context_, event.whois.nick);
+    dukx_push(context_, event.whois.nick);
     duk_put_prop_string(context_, -2, "nickname");
-    dukx_push_string(context_, event.whois.user);
+    dukx_push(context_, event.whois.user);
     duk_put_prop_string(context_, -2, "username");
-    dukx_push_string(context_, event.whois.realname);
+    dukx_push(context_, event.whois.realname);
     duk_put_prop_string(context_, -2, "realname");
-    dukx_push_string(context_, event.whois.host);
+    dukx_push(context_, event.whois.host);
     duk_put_prop_string(context_, -2, "host");
-    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);
-    }
-
+    dukx_push_array(context_, event.whois.channels.begin(), event.whois.channels.end());
     duk_put_prop_string(context_, -2, "channels");
 
     call("onWhois", 2);
--- a/libirccd-js/irccd/js/plugin_jsapi.cpp	Mon Nov 27 10:16:45 2017 +0100
+++ b/libirccd-js/irccd/js/plugin_jsapi.cpp	Mon Nov 27 15:03:38 2017 +0100
@@ -222,15 +222,15 @@
         return 0;
 
     duk_push_object(ctx);
-    dukx_push_string(ctx, plugin->name());
+    dukx_push(ctx, plugin->name());
     duk_put_prop_string(ctx, -2, "name");
-    dukx_push_string(ctx, plugin->author());
+    dukx_push(ctx, plugin->author());
     duk_put_prop_string(ctx, -2, "author");
-    dukx_push_string(ctx, plugin->license());
+    dukx_push(ctx, plugin->license());
     duk_put_prop_string(ctx, -2, "license");
-    dukx_push_string(ctx, plugin->summary());
+    dukx_push(ctx, plugin->summary());
     duk_put_prop_string(ctx, -2, "summary");
-    dukx_push_string(ctx, plugin->version());
+    dukx_push(ctx, plugin->version());
     duk_put_prop_string(ctx, -2, "version");
 
     return 1;
@@ -252,7 +252,7 @@
     duk_push_array(ctx);
 
     for (const auto& p : dukx_get_irccd(ctx).plugins().list()) {
-        dukx_push_string(ctx, p->name());
+        dukx_push(ctx, p->name());
         duk_put_prop_index(ctx, -2, i++);
     }
 
--- a/libirccd-js/irccd/js/server_jsapi.cpp	Mon Nov 27 10:16:45 2017 +0100
+++ b/libirccd-js/irccd/js/server_jsapi.cpp	Mon Nov 27 15:03:38 2017 +0100
@@ -101,9 +101,9 @@
     auto server = self(ctx);
 
     duk_push_object(ctx);
-    dukx_push_string(ctx, server->name());
+    dukx_push(ctx, server->name());
     duk_put_prop_string(ctx, -2, "name");
-    dukx_push_string(ctx, server->host());
+    dukx_push(ctx, server->host());
     duk_put_prop_string(ctx, -2, "host");
     duk_push_int(ctx, server->port());
     duk_put_prop_string(ctx, -2, "port");
@@ -111,23 +111,15 @@
     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_string(ctx, server->command_char());
+    dukx_push(ctx, server->command_char());
     duk_put_prop_string(ctx, -2, "commandChar");
-    dukx_push_string(ctx, server->realname());
+    dukx_push(ctx, server->realname());
     duk_put_prop_string(ctx, -2, "realname");
-    dukx_push_string(ctx, server->nickname());
+    dukx_push(ctx, server->nickname());
     duk_put_prop_string(ctx, -2, "nickname");
-    dukx_push_string(ctx, server->username());
+    dukx_push(ctx, server->username());
     duk_put_prop_string(ctx, -2, "username");
-
-    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++);
-    }
-
+    dukx_push_array(ctx, server->channels().begin(), server->channels().end());
     duk_put_prop_string(ctx, -2, "channels");
 
     return 1;
@@ -162,7 +154,7 @@
  */
 duk_ret_t join(duk_context* ctx)
 {
-    self(ctx)->join(duk_require_string(ctx, 0), dukx_get_string(ctx, 1));
+    self(ctx)->join(duk_require_string(ctx, 0), duk_require_string(ctx, 1));
 
     return 0;
 }
@@ -180,7 +172,7 @@
  */
 duk_ret_t kick(duk_context* ctx)
 {
-    self(ctx)->kick(duk_require_string(ctx, 0), duk_require_string(ctx, 1), dukx_get_string(ctx, 2));
+    self(ctx)->kick(duk_require_string(ctx, 0), duk_require_string(ctx, 1), duk_get_string(ctx, 2));
 
     return 0;
 }
@@ -296,7 +288,7 @@
  */
 duk_ret_t part(duk_context* ctx)
 {
-    self(ctx)->part(duk_require_string(ctx, 0), dukx_get_string(ctx, 1));
+    self(ctx)->part(duk_require_string(ctx, 0), duk_get_string(ctx, 1));
 
     return 0;
 }
@@ -362,7 +354,7 @@
  */
 duk_ret_t toString(duk_context* ctx)
 {
-    dukx_push_string(ctx, self(ctx)->name());
+    dukx_push(ctx, self(ctx)->name());
 
     return 1;
 }
--- a/libirccd-js/irccd/js/system_jsapi.cpp	Mon Nov 27 10:16:45 2017 +0100
+++ b/libirccd-js/irccd/js/system_jsapi.cpp	Mon Nov 27 15:03:38 2017 +0100
@@ -38,7 +38,7 @@
 namespace {
 
 /*
- * Function: irccd.System.env(key)
+ * Function: Irccd.System.env(key)
  * ------------------------------------------------------------------
  *
  * Get an environment system variable.
@@ -50,13 +50,11 @@
  */
 duk_ret_t env(duk_context* ctx)
 {
-    dukx_push_string(ctx, sys::env(dukx_get_string(ctx, 0)));
-
-    return 1;
+    return dukx_push(ctx, sys::env(duk_get_string(ctx, 0)));
 }
 
 /*
- * Function: irccd.System.exec(cmd)
+ * Function: Irccd.System.exec(cmd)
  * ------------------------------------------------------------------
  *
  * Execute a system command.
@@ -66,13 +64,13 @@
  */
 duk_ret_t exec(duk_context* ctx)
 {
-    std::system(duk_get_string(ctx, 0));
+    std::system(duk_require_string(ctx, 0));
 
     return 0;
 }
 
 /*
- * Function: irccd.System.home()
+ * Function: Irccd.System.home()
  * ------------------------------------------------------------------
  *
  * Get the operating system user's home.
@@ -82,13 +80,11 @@
  */
 duk_ret_t home(duk_context* ctx)
 {
-    dukx_push_string(ctx, sys::home());
-
-    return 1;
+    return dukx_push(ctx, sys::home());
 }
 
 /*
- * Function: irccd.System.name()
+ * Function: Irccd.System.name()
  * ------------------------------------------------------------------
  *
  * Get the operating system name.
@@ -98,15 +94,13 @@
  */
 duk_ret_t name(duk_context* ctx)
 {
-    dukx_push_string(ctx, sys::name());
-
-    return 1;
+    return dukx_push(ctx, sys::name());
 }
 
 #if defined(HAVE_POPEN)
 
 /*
- * Function: irccd.System.popen(cmd, mode) [optional]
+ * Function: Irccd.System.popen(cmd, mode) [optional]
  * ------------------------------------------------------------------
  *
  * Wrapper for popen(3) if the function is available.
@@ -134,7 +128,7 @@
 #endif // !HAVE_POPEN
 
 /*
- * Function: irccd.System.sleep(delay)
+ * Function: Irccd.System.sleep(delay)
  * ------------------------------------------------------------------
  *
  * Sleep the main loop for the specific delay in seconds.
@@ -147,7 +141,7 @@
 }
 
 /*
- * Function: irccd.System.ticks()
+ * Function: Irccd.System.ticks()
  * ------------------------------------------------------------------
  *
  * Get the number of milliseconds since irccd was started.
@@ -202,7 +196,7 @@
  */
 duk_ret_t version(duk_context* ctx)
 {
-    dukx_push_string(ctx, sys::version());
+    dukx_push(ctx, sys::version());
 
     return 1;
 }
--- a/libirccd-js/irccd/js/timer_jsapi.cpp	Mon Nov 27 10:16:45 2017 +0100
+++ b/libirccd-js/irccd/js/timer_jsapi.cpp	Mon Nov 27 15:03:38 2017 +0100
@@ -86,7 +86,7 @@
 
     if (duk_pcall(ctx, 0)) {
         log::warning() << "plugin: " << plugin->name() << " timer error:" << std::endl;
-        log::warning() << "  " << dukx_get_exception(ctx, -1).what() << std::endl;
+        log::warning() << "  " << dukx_stack(ctx, -1).what() << std::endl;
     } else
         duk_pop(ctx);
 }
--- a/libirccd-js/irccd/js/util_jsapi.cpp	Mon Nov 27 10:16:45 2017 +0100
+++ b/libirccd-js/irccd/js/util_jsapi.cpp	Mon Nov 27 15:03:38 2017 +0100
@@ -49,10 +49,14 @@
     duk_enum(ctx, index, 0);
 
     while (duk_next(ctx, -1, true)) {
-        if (dukx_get_string(ctx, -2) == "date")
+        if (dukx_get<std::string>(ctx, -2) == "date")
             params.time = static_cast<time_t>(duk_get_number(ctx, -1) / 1000);
         else
-            params.keywords.insert({dukx_get_string(ctx, -2), dukx_get_string(ctx, -1)});
+            params.keywords.insert({
+                dukx_get<std::string>(ctx, -2),
+                dukx_get<std::string>(ctx, -1)
+            });
+
 
         duk_pop_n(ctx, 2);
     }
@@ -74,7 +78,7 @@
     std::string pattern = " \t\n";
 
     if (duk_is_string(ctx, 0))
-        result = string_util::split(dukx_get_string(ctx, 0), pattern);
+        result = string_util::split(dukx_get<std::string>(ctx, 0), pattern);
     else if (duk_is_array(ctx, 0)) {
         duk_enum(ctx, 0, DUK_ENUM_ARRAY_INDICES_ONLY);
 
@@ -201,14 +205,7 @@
         return 1;
     }
 
-    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;
+    return dukx_push_array(ctx, list.begin(), list.end());
 }
 
 /*
@@ -226,7 +223,7 @@
 duk_ret_t format(duk_context* ctx)
 {
     try {
-        dukx_push_string(ctx, string_util::format(dukx_get_string(ctx, 0), get_subst(ctx, 1)));
+        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());
     }
@@ -247,7 +244,7 @@
  */
 duk_ret_t splituser(duk_context* ctx)
 {
-    dukx_push_string(ctx, irc::user::parse(duk_require_string(ctx, 0)).nick());
+    dukx_push(ctx, irc::user::parse(duk_require_string(ctx, 0)).nick());
 
     return 1;
 }
@@ -265,7 +262,7 @@
  */
 duk_ret_t splithost(duk_context* ctx)
 {
-    dukx_push_string(ctx, irc::user::parse(duk_require_string(ctx, 0)).host());
+    dukx_push(ctx, irc::user::parse(duk_require_string(ctx, 0)).host());
 
     return 1;
 }
--- a/tests/js-directory/main.cpp	Mon Nov 27 10:16:45 2017 +0100
+++ b/tests/js-directory/main.cpp	Mon Nov 27 15:03:38 2017 +0100
@@ -36,7 +36,7 @@
     );
 
     if (duk_peval_string(plugin_->context(), script.c_str()) != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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 10:16:45 2017 +0100
+++ b/tests/js-elapsedtimer/main.cpp	Mon Nov 27 15:03:38 2017 +0100
@@ -34,12 +34,12 @@
 BOOST_AUTO_TEST_CASE(standard)
 {
     if (duk_peval_string(plugin_->context(), "timer = new Irccd.ElapsedTimer();") != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     std::this_thread::sleep_for(300ms);
 
     if (duk_peval_string(plugin_->context(), "result = timer.elapsed();") != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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 10:16:45 2017 +0100
+++ b/tests/js-file/main.cpp	Mon Nov 27 15:03:38 2017 +0100
@@ -27,30 +27,12 @@
 
 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_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST("irccd.conf" == duk_get_string(plugin_->context(), -1));
@@ -59,7 +41,7 @@
 BOOST_AUTO_TEST_CASE(function_dirname)
 {
     if (duk_peval_string(plugin_->context(), "result = Irccd.File.dirname('/usr/local/etc/irccd.conf');"))
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST("/usr/local/etc" == duk_get_string(plugin_->context(), -1));
@@ -69,7 +51,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_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
@@ -78,7 +60,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_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST(!duk_get_boolean(plugin_->context(), -1));
@@ -90,7 +72,7 @@
     std::ofstream("test-js-fs.remove");
 
     if (duk_peval_string(plugin_->context(), "Irccd.File.remove('test-js-fs.remove');") != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     std::ifstream in("test-js-fs.remove");
 
@@ -105,7 +87,7 @@
     );
 
     if (ret != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST("file-1.txt" == duk_get_string(plugin_->context(), -1));
@@ -120,7 +102,7 @@
     );
 
     if (ret != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST("file-1.txt" == duk_get_string(plugin_->context(), -1));
@@ -134,7 +116,7 @@
     );
 
     if (ret != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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));
@@ -149,7 +131,7 @@
     );
 
     if (ret != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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));
@@ -162,12 +144,12 @@
     );
 
     if (ret != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     std::vector<std::string> expected{"a", "b", "c"};
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(expected == array(plugin_->context(), -1));
+    BOOST_TEST(expected == dukx_get_array<std::vector<std::string>>(plugin_->context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_seek1)
@@ -179,10 +161,10 @@
     );
 
     if (ret != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(".", dukx_get_string(plugin_->context(), -1));
+    BOOST_TEST(".", dukx_get<std::string>(plugin_->context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_seek1_closed)
@@ -196,7 +178,7 @@
     );
 
     if (ret != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
@@ -212,10 +194,10 @@
     );
 
     if (ret != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST("." == dukx_get_string(plugin_->context(), -1));
+    BOOST_TEST("." == dukx_get<std::string>(plugin_->context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_seek2c_losed)
@@ -230,7 +212,7 @@
     );
 
     if (ret != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
@@ -245,7 +227,7 @@
     );
 
     if (ret != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST("t" == duk_get_string(plugin_->context(), -1));
@@ -262,7 +244,7 @@
     );
 
     if (ret != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
@@ -276,7 +258,7 @@
     );
 
     if (ret != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
     BOOST_TEST("file-1.txt\n" == duk_get_string(plugin_->context(), -1));
@@ -293,12 +275,12 @@
     );
 
     if (ret != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     std::vector<std::string> expected{"a", "b", "c"};
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(expected == array(plugin_->context(), -1));
+    BOOST_TEST(expected == dukx_get_array<std::vector<std::string>>(plugin_->context(), -1));
 }
 
 BOOST_AUTO_TEST_CASE(method_readline_closed)
@@ -313,12 +295,12 @@
     );
 
     if (ret != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     std::vector<std::string> expected;
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "result"));
-    BOOST_TEST(expected == array(plugin_->context(), -1));
+    BOOST_TEST(expected == dukx_get_array<std::vector<std::string>>(plugin_->context(), -1));
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/js-irccd/main.cpp	Mon Nov 27 10:16:45 2017 +0100
+++ b/tests/js-irccd/main.cpp	Mon Nov 27 15:03:38 2017 +0100
@@ -34,7 +34,7 @@
     );
 
     if (ret != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "errno"));
     BOOST_TEST(1 == duk_get_int(plugin_->context(), -1));
@@ -73,7 +73,7 @@
     BOOST_TEST(duk_get_boolean(plugin_->context(), -1));
 }
 
-BOOST_AUTO_TEST_CASE(fromNative)
+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"));
@@ -96,7 +96,7 @@
     );
 
     if (ret != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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 10:16:45 2017 +0100
+++ b/tests/js-logger/main.cpp	Mon Nov 27 15:03:38 2017 +0100
@@ -72,7 +72,7 @@
 BOOST_AUTO_TEST_CASE(info)
 {
     if (duk_peval_string(plugin_->context(), "Irccd.Logger.info(\"hello!\");") != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     BOOST_TEST("plugin test: starting" == line_debug);
 }
--- a/tests/js-system/main.cpp	Mon Nov 27 10:16:45 2017 +0100
+++ b/tests/js-system/main.cpp	Mon Nov 27 15:03:38 2017 +0100
@@ -51,7 +51,7 @@
     );
 
     if (ret != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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 10:16:45 2017 +0100
+++ b/tests/js-util/main.cpp	Mon Nov 27 15:03:38 2017 +0100
@@ -39,7 +39,7 @@
     );
 
     if (ret != 0)
-        throw dukx_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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_get_exception(plugin_->context(), -1);
+        throw dukx_stack(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_get_exception(plugin_->context(), -1);
+        throw dukx_stack(plugin_->context(), -1);
 
     BOOST_TEST(duk_get_global_string(plugin_->context(), "name"));
     BOOST_TEST(duk_get_string(plugin_->context(), -1) == "TypeError");