changeset 567:cf734c969727

Irccd: convert to dukx_type_traits: - std::shared_ptr<server> - std::shared_ptr<file> - struct stat
author David Demelier <markand@malikania.fr>
date Mon, 27 Nov 2017 15:49:57 +0100
parents bf56628e070b
children ed986ae52656
files libirccd-js/irccd/js/duktape.hpp libirccd-js/irccd/js/file_jsapi.cpp libirccd-js/irccd/js/file_jsapi.hpp libirccd-js/irccd/js/js_plugin.cpp libirccd-js/irccd/js/server_jsapi.cpp libirccd-js/irccd/js/server_jsapi.hpp libirccd-js/irccd/js/system_jsapi.cpp
diffstat 7 files changed, 292 insertions(+), 163 deletions(-) [+]
line wrap: on
line diff
--- a/libirccd-js/irccd/js/duktape.hpp	Mon Nov 27 15:03:38 2017 +0100
+++ b/libirccd-js/irccd/js/duktape.hpp	Mon Nov 27 15:49:57 2017 +0100
@@ -23,7 +23,7 @@
  * \file duktape.hpp
  * \brief Miscellaneous Duktape extras
  * \author David Demelier <markand@malikania.fr>
- * \version 0.1.0
+ * \version 0.2.0
  */
 
 #include <cassert>
@@ -317,6 +317,20 @@
     {
         return duk_get_boolean(ctx, index);
     }
+
+    /**
+     * Require a boolean.
+     *
+     * Uses duk_require_boolean.
+     *
+     * \param ctx the Duktape context
+     * \param index the value index
+     * \return the converted value
+     */
+    static bool require(duk_context* ctx, duk_idx_t index)
+    {
+        return duk_require_boolean(ctx, index);
+    }
 };
 
 /**
@@ -351,6 +365,20 @@
     {
         return duk_get_number(ctx, index);
     }
+
+    /**
+     * Require a double.
+     *
+     * Uses duk_require_double.
+     *
+     * \param ctx the Duktape context
+     * \param index the value index
+     * \return the converted value
+     */
+    static duk_double_t require(duk_context* ctx, duk_idx_t index)
+    {
+        return duk_require_number(ctx, index);
+    }
 };
 
 /**
@@ -385,6 +413,20 @@
     {
         return duk_get_int(ctx, index);
     }
+
+    /**
+     * Require an int.
+     *
+     * Uses duk_require_int.
+     *
+     * \param ctx the Duktape context
+     * \param index the value index
+     * \return the converted value
+     */
+    static duk_int_t require(duk_context* ctx, duk_idx_t index)
+    {
+        return duk_require_int(ctx, index);
+    }
 };
 
 /**
@@ -419,6 +461,20 @@
     {
         return duk_get_uint(ctx, index);
     }
+
+    /**
+     * Require an unsigned int.
+     *
+     * Uses duk_require_uint.
+     *
+     * \param ctx the Duktape context
+     * \param index the value index
+     * \return the converted value
+     */
+    static duk_uint_t require(duk_context* ctx, duk_idx_t index)
+    {
+        return duk_require_uint(ctx, index);
+    }
 };
 
 /**
@@ -453,6 +509,20 @@
     {
         return duk_get_string(ctx, index);
     }
+
+    /**
+     * Require a C string.
+     *
+     * Uses duk_require_string.
+     *
+     * \param ctx the Duktape context
+     * \param index the value index
+     * \return the converted value
+     */
+    static const char* require(duk_context* ctx, duk_idx_t index)
+    {
+        return duk_require_string(ctx, index);
+    }
 };
 
 /**
@@ -490,6 +560,23 @@
 
         return {str, length};
     }
+
+    /**
+     * Require a C++ std::string.
+     *
+     * Uses duk_require_lstring.
+     *
+     * \param ctx the Duktape context
+     * \param index the value index
+     * \return the converted value
+     */
+    static std::string require(duk_context* ctx, duk_idx_t index)
+    {
+        duk_size_t length;
+        const char* str = duk_require_lstring(ctx, index, &length);
+
+        return {str, length};
+    }
 };
 
 /**
@@ -533,6 +620,25 @@
 }
 
 /**
+ * Generic require function.
+ *
+ * This functions calls dukx_type_traits<T>::require if specialized.
+ *
+ * \param ctx the Duktape context
+ * \param index the value index
+ * \return the converted value
+ */
+template <typename T>
+T dukx_require(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>::require(ctx, index);
+}
+
+/**
  * Push a Javascript array to the stack.
  *
  * This function push the value using duk_push_array and dukx_push for each
@@ -542,6 +648,7 @@
  * \param it the input iterator
  * \param end the end iterator
  * \return 1 for convenience
+ * \warning experimental and may change in the future
  */
 template <typename InputIt>
 int dukx_push_array(duk_context* ctx, InputIt it, InputIt end)
@@ -564,6 +671,7 @@
  * \param ctx the Duktape context
  * \param values the list of values
  * \return 1 for convenience
+ * \warning experimental and may change in the future
  */
 template <typename T>
 int dukx_push_array(duk_context* ctx, std::initializer_list<T> values)
@@ -583,6 +691,7 @@
  * \param it the input iterator
  * \param end the end iterator
  * \return 1 for convenience
+ * \warning experimental and may change in the future
  */
 template <typename InputIt>
 int dukx_push_object(duk_context* ctx, InputIt it, InputIt end)
@@ -606,6 +715,7 @@
  * \param ctx the Duktape context
  * \param values the list of key/value values
  * \return 1 for convenience
+ * \warning experimental and may change in the future
  */
 template <typename T>
 int dukx_push_object(duk_context* ctx, std::initializer_list<std::pair<std::string, T>> values)
@@ -622,6 +732,7 @@
  * \param ctx the Duktape context
  * \param index the array index
  * \param output the output iterator
+ * \warning experimental and may change in the future
  */
 template <typename T, typename OutputIt>
 void dukx_get_array(duk_context* ctx, duk_idx_t index, OutputIt output)
@@ -643,6 +754,7 @@
  * \param ctx the Duktape context
  * \param index the array index
  * \return the container of values (e.g. `std::vector<int>`)
+ * \warning experimental and may change in the future
  */
 template <typename Container>
 Container dukx_get_array(duk_context* ctx, duk_idx_t index)
@@ -662,6 +774,7 @@
  * \param ctx the Duktape context
  * \param index the object index
  * \return the container of values (e.g. `std::map<std::string, int>`)
+ * \warning experimental and may change in the future
  */
 template <typename Container>
 Container dukx_get_object(duk_context* ctx, duk_idx_t index)
--- a/libirccd-js/irccd/js/file_jsapi.cpp	Mon Nov 27 15:03:38 2017 +0100
+++ b/libirccd-js/irccd/js/file_jsapi.cpp	Mon Nov 27 15:49:57 2017 +0100
@@ -16,8 +16,6 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <irccd/sysconfig.hpp>
-
 #include <algorithm>
 #include <array>
 #include <cassert>
@@ -26,11 +24,6 @@
 
 #include <boost/filesystem.hpp>
 
-#if defined(HAVE_STAT)
-#  include <sys/types.h>
-#  include <sys/stat.h>
-#endif
-
 #include <irccd/fs_util.hpp>
 
 #include "file_jsapi.hpp"
@@ -44,75 +37,6 @@
 const char *signature("\xff""\xff""irccd-file-ptr");
 const char *prototype("\xff""\xff""irccd-file-prototype");
 
-#if defined(HAVE_STAT)
-
-/*
- * push_stat
- * ------------------------------------------------------------------
- */
-
-void push_stat(duk_context* ctx, const struct stat& st)
-{
-    dukx_stack_assert sa(ctx, 1);
-
-    duk_push_object(ctx);
-
-#if defined(HAVE_STAT_ST_ATIME)
-    duk_push_int(ctx, st.st_atime);
-    duk_put_prop_string(ctx, -2, "atime");
-#endif
-#if defined(HAVE_STAT_ST_BLKSIZE)
-    duk_push_int(ctx, st.st_blksize);
-    duk_put_prop_string(ctx, -2, "blksize");
-#endif
-#if defined(HAVE_STAT_ST_BLOCKS)
-    duk_push_int(ctx, st.st_blocks);
-    duk_put_prop_string(ctx, -2, "blocks");
-#endif
-#if defined(HAVE_STAT_ST_CTIME)
-    duk_push_int(ctx, st.st_ctime);
-    duk_put_prop_string(ctx, -2, "ctime");
-#endif
-#if defined(HAVE_STAT_ST_DEV)
-    duk_push_int(ctx, st.st_dev);
-    duk_put_prop_string(ctx, -2, "dev");
-#endif
-#if defined(HAVE_STAT_ST_GID)
-    duk_push_int(ctx, st.st_gid);
-    duk_put_prop_string(ctx, -2, "gid");
-#endif
-#if defined(HAVE_STAT_ST_INO)
-    duk_push_int(ctx, st.st_ino);
-    duk_put_prop_string(ctx, -2, "ino");
-#endif
-#if defined(HAVE_STAT_ST_MODE)
-    duk_push_int(ctx, st.st_mode);
-    duk_put_prop_string(ctx, -2, "mode");
-#endif
-#if defined(HAVE_STAT_ST_MTIME)
-    duk_push_int(ctx, st.st_mtime);
-    duk_put_prop_string(ctx, -2, "mtime");
-#endif
-#if defined(HAVE_STAT_ST_NLINK)
-    duk_push_int(ctx, st.st_nlink);
-    duk_put_prop_string(ctx, -2, "nlink");
-#endif
-#if defined(HAVE_STAT_ST_RDEV)
-    duk_push_int(ctx, st.st_rdev);
-    duk_put_prop_string(ctx, -2, "rdev");
-#endif
-#if defined(HAVE_STAT_ST_SIZE)
-    duk_push_int(ctx, st.st_size);
-    duk_put_prop_string(ctx, -2, "size");
-#endif
-#if defined(HAVE_STAT_ST_UID)
-    duk_push_int(ctx, st.st_uid);
-    duk_put_prop_string(ctx, -2, "uid");
-#endif
-}
-
-#endif // !HAVE_STAT
-
 // Remove trailing \r for CRLF line style.
 inline std::string clear_crlf(std::string input)
 {
@@ -122,19 +46,19 @@
     return input;
 }
 
-file* self(duk_context* ctx)
+std::shared_ptr<file> self(duk_context* ctx)
 {
     dukx_stack_assert sa(ctx);
 
     duk_push_this(ctx);
     duk_get_prop_string(ctx, -1, signature);
-    auto ptr = static_cast<file*>(duk_to_pointer(ctx, -1));
+    auto ptr = static_cast<std::shared_ptr<file>*>(duk_to_pointer(ctx, -1));
     duk_pop_2(ctx);
 
     if (!ptr)
         duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a File object");
 
-    return ptr;
+    return *ptr;
 }
 
 /*
@@ -371,7 +295,7 @@
     if (file->handle() == nullptr && ::stat(file->path().c_str(), &st) < 0)
         dukx_throw(ctx, system_error());
     else
-        push_stat(ctx, st);
+        dukx_push(ctx, st);
 
     return 1;
 }
@@ -476,8 +400,14 @@
         return 0;
 
     try {
-        dukx_new_file(ctx, new file(duk_require_string(ctx, 0), duk_require_string(ctx, 1)));
-    } catch (const std::exception &) {
+        auto path = dukx_require<std::string>(ctx, 0);
+        auto mode = dukx_require<std::string>(ctx, 1);
+
+        duk_push_this(ctx);
+        duk_push_pointer(ctx, new std::shared_ptr<file>(new file(path, mode)));
+        duk_put_prop_string(ctx, -2, signature);
+        duk_pop(ctx);
+    } catch (const std::exception&) {
         dukx_throw(ctx, system_error());
     }
 
@@ -493,7 +423,7 @@
 duk_ret_t destructor(duk_context* ctx)
 {
     duk_get_prop_string(ctx, 0, signature);
-    delete static_cast<file*>(duk_to_pointer(ctx, -1));
+    delete static_cast<std::shared_ptr<file>*>(duk_to_pointer(ctx, -1));
     duk_pop(ctx);
     duk_del_prop_string(ctx, 0, signature);
 
@@ -597,9 +527,7 @@
     if (::stat(duk_require_string(ctx, 0), &st) < 0)
         dukx_throw(ctx, system_error());
 
-    push_stat(ctx, st);
-
-    return 1;
+    return dukx_push(ctx, st);
 }
 
 #endif // !HAVE_STAT
@@ -648,20 +576,9 @@
     duk_pop(plugin->context());
 }
 
-void dukx_new_file(duk_context* ctx, file* fp)
-{
-    assert(ctx);
-    assert(fp);
-
-    dukx_stack_assert sa(ctx);
+using file_traits = dukx_type_traits<std::shared_ptr<file>>;
 
-    duk_push_this(ctx);
-    duk_push_pointer(ctx, fp);
-    duk_put_prop_string(ctx, -2, signature);
-    duk_pop(ctx);
-}
-
-void dukx_push_file(duk_context* ctx, file* fp)
+void file_traits::push(duk_context* ctx, std::shared_ptr<file> fp)
 {
     assert(ctx);
     assert(fp);
@@ -669,22 +586,86 @@
     dukx_stack_assert sa(ctx, 1);
 
     duk_push_object(ctx);
-    duk_push_pointer(ctx, fp);
+    duk_push_pointer(ctx, new std::shared_ptr<file>(std::move(fp)));
     duk_put_prop_string(ctx, -2, signature);
     duk_get_global_string(ctx, prototype);
     duk_set_prototype(ctx, -2);
 }
 
-file* dukx_require_file(duk_context* ctx, duk_idx_t index)
+std::shared_ptr<file> file_traits::require(duk_context* ctx, duk_idx_t index)
 {
     if (!duk_is_object(ctx, index) || !duk_has_prop_string(ctx, index, signature))
         duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a File object");
 
     duk_get_prop_string(ctx, index, signature);
-    auto fp = static_cast<file*>(duk_to_pointer(ctx, -1));
+    auto fp = static_cast<std::shared_ptr<file>*>(duk_to_pointer(ctx, -1));
     duk_pop(ctx);
 
-    return fp;
+    return *fp;
 }
 
+#if defined(HAVE_STAT)
+
+void dukx_type_traits<struct stat>::push(duk_context* ctx, const struct stat& st)
+{
+    dukx_stack_assert sa(ctx, 1);
+
+    duk_push_object(ctx);
+
+#if defined(HAVE_STAT_ST_ATIME)
+    duk_push_int(ctx, st.st_atime);
+    duk_put_prop_string(ctx, -2, "atime");
+#endif
+#if defined(HAVE_STAT_ST_BLKSIZE)
+    duk_push_int(ctx, st.st_blksize);
+    duk_put_prop_string(ctx, -2, "blksize");
+#endif
+#if defined(HAVE_STAT_ST_BLOCKS)
+    duk_push_int(ctx, st.st_blocks);
+    duk_put_prop_string(ctx, -2, "blocks");
+#endif
+#if defined(HAVE_STAT_ST_CTIME)
+    duk_push_int(ctx, st.st_ctime);
+    duk_put_prop_string(ctx, -2, "ctime");
+#endif
+#if defined(HAVE_STAT_ST_DEV)
+    duk_push_int(ctx, st.st_dev);
+    duk_put_prop_string(ctx, -2, "dev");
+#endif
+#if defined(HAVE_STAT_ST_GID)
+    duk_push_int(ctx, st.st_gid);
+    duk_put_prop_string(ctx, -2, "gid");
+#endif
+#if defined(HAVE_STAT_ST_INO)
+    duk_push_int(ctx, st.st_ino);
+    duk_put_prop_string(ctx, -2, "ino");
+#endif
+#if defined(HAVE_STAT_ST_MODE)
+    duk_push_int(ctx, st.st_mode);
+    duk_put_prop_string(ctx, -2, "mode");
+#endif
+#if defined(HAVE_STAT_ST_MTIME)
+    duk_push_int(ctx, st.st_mtime);
+    duk_put_prop_string(ctx, -2, "mtime");
+#endif
+#if defined(HAVE_STAT_ST_NLINK)
+    duk_push_int(ctx, st.st_nlink);
+    duk_put_prop_string(ctx, -2, "nlink");
+#endif
+#if defined(HAVE_STAT_ST_RDEV)
+    duk_push_int(ctx, st.st_rdev);
+    duk_put_prop_string(ctx, -2, "rdev");
+#endif
+#if defined(HAVE_STAT_ST_SIZE)
+    duk_push_int(ctx, st.st_size);
+    duk_put_prop_string(ctx, -2, "size");
+#endif
+#if defined(HAVE_STAT_ST_UID)
+    duk_push_int(ctx, st.st_uid);
+    duk_put_prop_string(ctx, -2, "uid");
+#endif
+}
+
+#endif // !HAVE_STAT
+
 } // !irccd
--- a/libirccd-js/irccd/js/file_jsapi.hpp	Mon Nov 27 15:03:38 2017 +0100
+++ b/libirccd-js/irccd/js/file_jsapi.hpp	Mon Nov 27 15:49:57 2017 +0100
@@ -24,6 +24,13 @@
  * \brief Irccd.File Javascript API.
  */
 
+#include <irccd/sysconfig.hpp>
+
+#if defined(HAVE_STAT)
+#   include <sys/types.h>
+#   include <sys/stat.h>
+#endif
+
 #include <cassert>
 #include <cerrno>
 #include <cstdio>
@@ -150,33 +157,52 @@
 };
 
 /**
- * Construct the file as this.
- *
- * The object prototype takes ownership of fp and will be deleted once
- * collected.
+ * \brief Specialization for generic file type as shared_ptr.
  *
- * \pre fp != nullptr
- * \param ctx the the context
- * \param fp the file
+ * Supports push, require.
  */
-void dukx_new_file(duk_context* ctx, file* fp);
+template <>
+class dukx_type_traits<std::shared_ptr<file>> : public std::true_type {
+public:
+    /**
+     * Push a file.
+     *
+     * \pre fp != nullptr
+     * \param ctx the the context
+     * \param fp the file
+     */
+    static void push(duk_context* ctx, std::shared_ptr<file> fp);
+
+    /**
+     * Require a file. Raises a JavaScript error if not a File.
+     *
+     * \param ctx the context
+     * \param index the index
+     * \return the file pointer
+     */
+    static std::shared_ptr<file> require(duk_context* ctx, duk_idx_t index);
+};
+
+#if defined(HAVE_STAT)
 
 /**
- * Push a file.
+ * \brief Specialization for struct stat.
  *
- * \pre fp != nullptr
- * \param ctx the the context
- * \param fp the file
+ * Supports push.
  */
-void dukx_push_file(duk_context* ctx, file* fp);
+template <>
+class dukx_type_traits<struct stat> : public std::true_type {
+public:
+    /**
+     * Push the stat information to the stack as Javascript object.
+     *
+     * \param ctx the context
+     * \param st the stat structure
+     */
+    static void push(duk_context* ctx, const struct stat& st);
+};
 
-/**
- * Require a file. Raises a JavaScript error if not a File.
- *
- * \param ctx the context
- * \param index the index
- */
-file* dukx_require_file(duk_context* ctx, duk_idx_t index);
+#endif // !HAVE_STAT
 
 } // !irccd
 
--- a/libirccd-js/irccd/js/js_plugin.cpp	Mon Nov 27 15:03:38 2017 +0100
+++ b/libirccd-js/irccd/js/js_plugin.cpp	Mon Nov 27 15:49:57 2017 +0100
@@ -150,7 +150,7 @@
 {
     dukx_stack_assert sa(context_);
 
-    dukx_push_server(context_, std::move(event.server));
+    dukx_push(context_, std::move(event.server));
     dukx_push(context_, event.origin);
     dukx_push(context_, event.channel);
     dukx_push(context_, event.mode);
@@ -162,7 +162,7 @@
 {
     dukx_stack_assert sa(context_);
 
-    dukx_push_server(context_, std::move(event.server));
+    dukx_push(context_, std::move(event.server));
     dukx_push(context_, event.origin);
     dukx_push(context_, event.channel);
     dukx_push(context_, event.message);
@@ -173,7 +173,7 @@
 {
     dukx_stack_assert sa(context_);
 
-    dukx_push_server(context_, std::move(event.server));
+    dukx_push(context_, std::move(event.server));
     dukx_push(context_, event.origin);
     dukx_push(context_, event.channel);
     dukx_push(context_, event.message);
@@ -184,7 +184,7 @@
 {
     dukx_stack_assert sa(context_);
 
-    dukx_push_server(context_, std::move(event.server));
+    dukx_push(context_, std::move(event.server));
     call("onConnect", 1);
 }
 
@@ -192,7 +192,7 @@
 {
     dukx_stack_assert sa(context_);
 
-    dukx_push_server(context_, std::move(event.server));
+    dukx_push(context_, std::move(event.server));
     dukx_push(context_, event.origin);
     dukx_push(context_, event.channel);
     call("onInvite", 3);
@@ -202,7 +202,7 @@
 {
     dukx_stack_assert sa(context_);
 
-    dukx_push_server(context_, std::move(event.server));
+    dukx_push(context_, std::move(event.server));
     dukx_push(context_, event.origin);
     dukx_push(context_, event.channel);
     call("onJoin", 3);
@@ -212,7 +212,7 @@
 {
     dukx_stack_assert sa(context_);
 
-    dukx_push_server(context_, std::move(event.server));
+    dukx_push(context_, std::move(event.server));
     dukx_push(context_, event.origin);
     dukx_push(context_, event.channel);
     dukx_push(context_, event.target);
@@ -231,7 +231,7 @@
 {
     dukx_stack_assert sa(context_);
 
-    dukx_push_server(context_, std::move(event.server));
+    dukx_push(context_, std::move(event.server));
     dukx_push(context_, event.origin);
     dukx_push(context_, event.channel);
     dukx_push(context_, event.message);
@@ -242,7 +242,7 @@
 {
     dukx_stack_assert sa(context_);
 
-    dukx_push_server(context_, std::move(event.server));
+    dukx_push(context_, std::move(event.server));
     dukx_push(context_, event.origin);
     dukx_push(context_, event.channel);
     dukx_push(context_, event.message);
@@ -253,7 +253,7 @@
 {
     dukx_stack_assert sa(context_);
 
-    dukx_push_server(context_, std::move(event.server));
+    dukx_push(context_, std::move(event.server));
     dukx_push(context_, event.origin);
     dukx_push(context_, event.mode);
     call("onMode", 3);
@@ -263,7 +263,7 @@
 {
     dukx_stack_assert sa(context_);
 
-    dukx_push_server(context_, std::move(event.server));
+    dukx_push(context_, std::move(event.server));
     dukx_push(context_, event.channel);
     dukx_push_array(context_, event.names.begin(), event.names.end());
 
@@ -274,7 +274,7 @@
 {
     dukx_stack_assert sa(context_);
 
-    dukx_push_server(context_, std::move(event.server));
+    dukx_push(context_, std::move(event.server));
     dukx_push(context_, event.origin);
     dukx_push(context_, event.nickname);
     call("onNick", 3);
@@ -284,7 +284,7 @@
 {
     dukx_stack_assert sa(context_);
 
-    dukx_push_server(context_, std::move(event.server));
+    dukx_push(context_, std::move(event.server));
     dukx_push(context_, event.origin);
     dukx_push(context_, event.message);
     call("onNotice", 3);
@@ -294,7 +294,7 @@
 {
     dukx_stack_assert sa(context_);
 
-    dukx_push_server(context_, std::move(event.server));
+    dukx_push(context_, std::move(event.server));
     dukx_push(context_, event.origin);
     dukx_push(context_, event.channel);
     dukx_push(context_, event.reason);
@@ -305,7 +305,7 @@
 {
     dukx_stack_assert sa(context_);
 
-    dukx_push_server(context_, std::move(event.server));
+    dukx_push(context_, std::move(event.server));
     dukx_push(context_, event.origin);
     dukx_push(context_, event.message);
     call("onQuery", 3);
@@ -315,7 +315,7 @@
 {
     dukx_stack_assert sa(context_);
 
-    dukx_push_server(context_, std::move(event.server));
+    dukx_push(context_, std::move(event.server));
     dukx_push(context_, event.origin);
     dukx_push(context_, event.message);
     call("onQueryCommand", 3);
@@ -332,7 +332,7 @@
 {
     dukx_stack_assert sa(context_);
 
-    dukx_push_server(context_, std::move(event.server));
+    dukx_push(context_, std::move(event.server));
     dukx_push(context_, event.origin);
     dukx_push(context_, event.channel);
     dukx_push(context_, event.topic);
@@ -350,7 +350,7 @@
 {
     dukx_stack_assert sa(context_);
 
-    dukx_push_server(context_, std::move(event.server));
+    dukx_push(context_, std::move(event.server));
     duk_push_object(context_);
     dukx_push(context_, event.whois.nick);
     duk_put_prop_string(context_, -2, "nickname");
--- a/libirccd-js/irccd/js/server_jsapi.cpp	Mon Nov 27 15:03:38 2017 +0100
+++ b/libirccd-js/irccd/js/server_jsapi.cpp	Mon Nov 27 15:49:57 2017 +0100
@@ -429,7 +429,7 @@
  */
 duk_ret_t add(duk_context* ctx)
 {
-    dukx_get_irccd(ctx).servers().add(dukx_require_server(ctx, 0));
+    dukx_get_irccd(ctx).servers().add(dukx_require<std::shared_ptr<server>>(ctx, 0));
 
     return 0;
 }
@@ -452,7 +452,7 @@
     if (!server)
         return 0;
 
-    dukx_push_server(ctx, server);
+    dukx_push(ctx, server);
 
     return 1;
 }
@@ -471,7 +471,7 @@
     duk_push_object(ctx);
 
     for (const auto &server : dukx_get_irccd(ctx).servers().servers()) {
-        dukx_push_server(ctx, server);
+        dukx_push(ctx, server);
         duk_put_prop_string(ctx, -2, server->name().c_str());
     }
 
@@ -549,7 +549,9 @@
     duk_pop(plugin->context());
 }
 
-void dukx_push_server(duk_context* ctx, std::shared_ptr<server> server)
+using server_traits = dukx_type_traits<std::shared_ptr<server>>;
+
+void server_traits::push(duk_context* ctx, std::shared_ptr<server> server)
 {
     assert(ctx);
     assert(server);
@@ -563,7 +565,7 @@
     duk_set_prototype(ctx, -2);
 }
 
-std::shared_ptr<server> dukx_require_server(duk_context* ctx, duk_idx_t index)
+std::shared_ptr<server> server_traits::require(duk_context* ctx, duk_idx_t index)
 {
     if (!duk_is_object(ctx, index) || !duk_has_prop_string(ctx, index, signature))
         duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Server object");
--- a/libirccd-js/irccd/js/server_jsapi.hpp	Mon Nov 27 15:03:38 2017 +0100
+++ b/libirccd-js/irccd/js/server_jsapi.hpp	Mon Nov 27 15:49:57 2017 +0100
@@ -47,22 +47,31 @@
 };
 
 /**
- * Push a server.
+ * \brief Specialization for servers as shared_ptr.
  *
- * \pre server != nullptr
- * \param ctx the context
- * \param server the server
+ * Supports push, require.
  */
-void dukx_push_server(duk_context* ctx, std::shared_ptr<server> server);
+template <>
+class dukx_type_traits<std::shared_ptr<server>> : public std::true_type {
+public:
+    /**
+     * Push a server.
+     *
+     * \pre server != nullptr
+     * \param ctx the context
+     * \param server the server
+     */
+    static void push(duk_context* ctx, std::shared_ptr<server> server);
 
-/**
- * Require a server. Raise a Javascript error if not a Server.
- *
- * \param ctx the context
- * \param index the index
- * \return the server
- */
-std::shared_ptr<server> dukx_require_server(duk_context* ctx, duk_idx_t index);
+    /**
+     * Require a server. Raise a Javascript error if not a Server.
+     *
+     * \param ctx the context
+     * \param index the index
+     * \return the server
+     */
+    static std::shared_ptr<server> require(duk_context* ctx, duk_idx_t index);
+};
 
 } // !irccd
 
--- a/libirccd-js/irccd/js/system_jsapi.cpp	Mon Nov 27 15:03:38 2017 +0100
+++ b/libirccd-js/irccd/js/system_jsapi.cpp	Mon Nov 27 15:49:57 2017 +0100
@@ -120,9 +120,7 @@
     if (fp == nullptr)
         dukx_throw(ctx, system_error());
 
-    dukx_push_file(ctx, new file(fp, [] (auto fp) { ::pclose(fp); }));
-
-    return 1;
+    return dukx_push(ctx, std::make_shared<file>(fp, [] (auto fp) { ::pclose(fp); }));
 }
 
 #endif // !HAVE_POPEN