Mercurial > irccd
changeset 179:ef527409e638
Irccd: update JavaScript code
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 31 May 2016 21:03:01 +0200 |
parents | 084081b24024 |
children | b0909b31eccb |
files | cmake/internal/sysconfig.hpp.in irccd/main.cpp lib/irccd/js.hpp lib/irccd/mod-directory.cpp lib/irccd/mod-elapsed-timer.cpp lib/irccd/mod-elapsed-timer.hpp lib/irccd/mod-file.cpp lib/irccd/mod-file.hpp lib/irccd/mod-irccd.cpp lib/irccd/mod-irccd.hpp lib/irccd/mod-logger.cpp lib/irccd/mod-plugin.cpp lib/irccd/mod-plugin.hpp lib/irccd/mod-server.cpp lib/irccd/mod-server.hpp lib/irccd/mod-system.cpp lib/irccd/mod-timer.cpp lib/irccd/mod-unicode.cpp lib/irccd/mod-util.cpp lib/irccd/plugin-js.cpp lib/irccd/plugin-js.hpp lib/irccd/server-event.cpp tests/js-elapsedtimer/main.cpp tests/js-file/main.cpp tests/js-irccd/main.cpp tests/js-logger/main.cpp tests/js-system/CMakeLists.txt tests/js-system/main.cpp tests/js-util/main.cpp |
diffstat | 29 files changed, 1580 insertions(+), 920 deletions(-) [+] |
line wrap: on
line diff
--- a/cmake/internal/sysconfig.hpp.in Thu May 26 21:20:46 2016 +0200 +++ b/cmake/internal/sysconfig.hpp.in Tue May 31 21:03:01 2016 +0200 @@ -37,6 +37,7 @@ #define IRCCD_VERSION_MAJOR @IRCCD_VERSION_MAJOR@ #define IRCCD_VERSION_MINOR @IRCCD_VERSION_MINOR@ #define IRCCD_VERSION_PATCH @IRCCD_VERSION_PATCH@ +#define IRCCD_VERSION "@IRCCD_VERSION@" /* * System identification.
--- a/irccd/main.cpp Thu May 26 21:20:46 2016 +0200 +++ b/irccd/main.cpp Tue May 31 21:03:01 2016 +0200 @@ -31,6 +31,7 @@ #endif #include <csignal> +#include <iostream> #include <format.h> @@ -61,7 +62,8 @@ log::warning() << " -f, --foreground do not run as a daemon\n"; log::warning() << " --help show this help\n"; log::warning() << " -p, --plugin name load a specific plugin\n"; - log::warning() << " -v, --verbose be verbose" << std::endl; + log::warning() << " -v, --verbose be verbose\n"; + log::warning() << " --version show the version\n"; std::exit(1); } @@ -103,20 +105,22 @@ { "-p", true }, { "--plugin", true }, { "-v", false }, - { "--verbose", false } + { "--verbose", false }, + { "--version", false } }; result = parser::read(argc, argv, options); for (const auto &pair : result) { - if (pair.first == "--help") { + if (pair.first == "--help") usage(); // NOTREACHED + if (pair.first == "--version") { + std::cout << IRCCD_VERSION << std::endl; + std::exit(1); } - - if (pair.first == "-v" || pair.first == "--verbose") { + if (pair.first == "-v" || pair.first == "--verbose") log::setVerbose(true); - } } } catch (const std::exception &ex) { log::warning() << sys::programName() << ": " << ex.what() << std::endl; @@ -143,17 +147,15 @@ void loadPid(const std::string &path) { - if (path.empty()) { + if (path.empty()) return; - } try { #if defined(HAVE_GETPID) std::ofstream out(path, std::ofstream::trunc); - if (!out) { + if (!out) throw std::runtime_error("could not open pidfile {}: {}"_format(path, std::strerror(errno))); - } log::debug() << "irccd: pid written in " << path << std::endl; out << getpid() << std::endl; @@ -168,13 +170,12 @@ void loadGid(const std::string gid) { try { - if (!gid.empty()) { + if (!gid.empty()) #if defined(HAVE_SETGID) sys::setGid(gid); #else throw std::runtime_error(" gid option not supported on this platform"); #endif - } } catch (const std::exception &ex) { log::warning() << "irccd: " << ex.what() << std::endl; } @@ -183,13 +184,12 @@ void loadUid(const std::string &uid) { try { - if (!uid.empty()) { + if (!uid.empty()) #if defined(HAVE_SETUID) sys::setUid(uid); #else throw std::runtime_error("uid option not supported on this platform"); #endif - } } catch (const std::exception &ex) { log::warning() << "irccd: " << ex.what() << std::endl; } @@ -199,9 +199,8 @@ { try { #if defined(HAVE_DAEMON) - if (options.count("-f") == 0 && options.count("--foreground") == 0 && !foreground) { + if (options.count("-f") == 0 && options.count("--foreground") == 0 && !foreground) daemon(1, 0); - } #else if (options.count("-f") > 0 || options.count("--foreground") > 0 || foreground) throw std::runtime_error("foreground option not supported on this platform"); @@ -233,19 +232,16 @@ loadForeground(config.isForeground(), options); // [transport] - for (const auto &transport : config.loadTransports()) { + for (const auto &transport : config.loadTransports()) instance->transportService().add(transport); - } // [server] section. - for (const auto &server : config.loadServers()) { + for (const auto &server : config.loadServers()) instance->serverService().add(server); - } // [rule] section. - for (const auto &rule : config.loadRules()) { + for (const auto &rule : config.loadRules()) instance->ruleService().add(rule); - } // [plugin] section. config.loadPlugins(*instance);
--- a/lib/irccd/js.hpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/js.hpp Tue May 31 21:03:01 2016 +0200 @@ -22,6 +22,7 @@ /** * \file js.hpp * \brief Bring JavaScript using Duktape. + * \author David Demelier <markand@malikania.fr> * * This file provides usual Duktape function renamed and placed into `duk` namespace. It also replaces error * code with exceptions when possible. @@ -29,7 +30,435 @@ * For convenience, this file also provides templated functions, overloads and much more. */ +/** + * \page js JavaScript binding + * \brief JavaScript binding using Duktape + * + * This page will show you how to setup this module to host JavaScript environment into your C++ application. + * + * ## Duktape + * + * Duktape is a C library designed for performance, small footprint and conformance. This wrapper is built top of it and + * requires it at build and runtime. + * + * It is highly recommended that you read the [Duktape guide](http://duktape.org/guide.html) before continuing because + * a lot of concepts are kept as-is. + * + * ## Installation + * + * You need the Duktape source amalgamation, it is provided with this module. + * + * When compiling, be sure to enable `-DDUK_OPT_CPP_EXCEPTIONS` and that Duktape source file has **cpp** extension. + * + * Just copy the **js.hpp** file and include it into your project. The header depends on **duktape.h** so be sure to + * provide it when compiling. + * + * - \subpage js-init + * - \subpage js-types + * - \subpage js-basics + * - \subpage js-more + */ + +/** + * \page js-init Initialization + * \brief Context initialization. + * + * To host JavaScript, you need a context. Usually one is sufficient but you can create as many as you want but they + * won't share any resource. + * + * \code + * #include "js.hpp" + * + * int main() + * { + * duk::UniqueContext ctx; + * + * return 0; + * } + * \endcode + * + * The duk::UniqueContext class is a RAII based wrapper around the native duk_context structure. It is automatically created and closed + * in the destructor. + * + * Be sure to not keep any pointer to it. + */ + +/** + * \page js-types Predefined types + * \brief Default duk::TypeTraits specializations + * + * The following specializations are provided with libjs. + * + * ## Primitive types + * + * | Type | Support | Remarks | + * |----------------|----------------------------------|---------------------------------------| + * | `int` | get, is, optional, push, require | | + * | `bool` | get, is, optional, push, require | | + * | `double` | get, is, optional, push, require | | + * | `std::string` | get, is, optional, push, require | can contain '\0' and binary data | + * | `const char *` | get, is, optional, push, require | | + * | `unsigned` | get, is, optional, push, require | | + * | T * | get, is, optional, push, require | raw pointer, never deleted by Duktape | + * + * ## Special JavaScript types + * + * The following types are used to create or inspect JavaScript types. + * + * | Type | Support | Remarks | + * |------------------|----------|----------------------------------------| + * | duk::Array | is, push | | + * | duk::Function | is, push | is just check if the value is callable | + * | duk::FunctionMap | put | | + * | duk::Global | push | | + * | duk::Null | is, push | | + * | duk::Object | is, push | | + * | duk::This | push | | + * | duk::Undefined | is, push | | + * + * ## Partial specializations + * + * These partial specializations will use complete specialization for T. + * + * | Type | Support | Remarks | + * |------------------------------------|-----------|------------------------| + * | std::unordered_map<std::string, T> | push, put | push or put properties | + * | std::vector<T> | push, put | push array of values | + */ + +/** + * \page js-basics Basics + * \brief Basics use case. + * + * The following topics are sample use case of the C++ front end. It does not use extended features that this module + * provides. + * + * - \subpage js-basics-t1 + * - \subpage js-basics-t2 + * - \subpage js-basics-t3 + */ + +/** + * \page js-basics-t1 Example 1: call JavaScript code from C++ + * \brief Evaluate JavaScript code from C++. + * + * Let use JavaScript to compute a simple expression. + * + * \code + * #include "js.hpp" + * + * int main() + * { + * duk::UniqueContext ctx; + * + * duk::pevalString(ctx, "1 + 1"); + * std::cout << duk::get<int>(ctx -1) << std::endl; + * + * return 0; + * } + * \endcode + */ + +/** + * \page js-basics-t2 Example 2: call C++ code from JavaScript + * \brief Evaluate a function from JavaScript. + * + * In that example, we will add a C++ function to JavaScript and call it from the script. The function just compute the + * two arguments that are passed through the function and return the result. + * + * We take the benefits of C++11 to map the function. The lambda can not have a capture because Duktape use raw C + * function pointers and this module keep them. + * + * \code + * #include "js.hpp" + * + * int main() + * { + * duk::UniqueContext ctx; + * + * // Push a function as global "add" + * duk::putGlobal(ctx, "add", duk::Function{[] (duk::Context *ctx) -> duk::Ret { + * int x = duk::require<int>(ctx, 0); + * int y = duk::require<int>(ctx, 1); + * + * duk::push(ctx, x + y); + * + * return 1; + * }, 2}); + * + * // Evaluate from JavaScript + * duk::pevalString(ctx, "add(1, 2)"); + * + * return 0; + * } + * \endcode + * + * Please note the **2** at end of lambda which indicates that the function takes 2 arguments. If number of arguments + * is variadic, pass DUK_VARARGS there. + */ + +/** + * \page js-basics-t3 Example 3: pushing and getting values + * \brief Manage values between JavaScript and C++. + * + * As you have seen in the previous examples, we were pushing some values to the Duktape context. + * + * With the C Duktape frontend, you usually use duk_push_int, duk_push_string and all other related functions. The libjs + * module provides an uniform and convenient way for sharing values with the same functions. + * + * See the description of duk::TypeTraits to see the supported types. + * + * ## Push + * + * The duk::push function is a template that accept most of the primitives types. It uses the specializations of the + * duk::TypeTraits class (described later). + * + * Example of code + * + * \code + * duk::push(ctx, 123); // an integer + * duk::push(ctx, true); // a boolean + * \endcode + * + * The push function add a new value to the stack for any specialization of TypeTraits provided by libjs. + * + * ## Get + * + * The duk::get function is similar to duk_get_ functions. It usually does not check the value and return a sane default + * value. + * + * This template function does not take the template argument so it can't be deduced and must be specified explicitly. + * + * \code + * duk::get<int>(ctx, 0) // get an int at index 0 + * duk::get<std::string>(ctx, 1) // get a std::string at index 1 + * \endcode + * + * ## Require + * + * The duk::require function is similar to duk_require functions. It requires the exact type at the given index. If the + * value is invalid a JavaScript error is propagated. + * + * \code + * duk::require<int>(ctx, 0) // require an int at index 0 or raise an error + * \endcode + * + * ## Put + * + * This special function is similar to push except that it applies to the existing object at the top of the stack. It + * is usually implemented for map and vector. + * + * \code + * // Fill the object at the top of the stack with this map + * std:unordered_map<std::string, int> map{ + * { "value1", 1 }, + * { "value2", 2 } + * }; + * + * duk::put(ctx, map); + * \endcode + * + * ## Is + * + * This function duk::is checks if the value at the given index is of the given type and return true. + * + * Just like duk::get, this function need the explicit template parameter. + * + * \code + * duk::push(ctx, 1); + * duk::is<int>(ctx, -1); // true + * \endcode + * + * ## Optional + * + * The duk::optional function has no equivalent in Duktape C API. It is a convenient way to get values with a default + * replacement is not available. + * + * The common implementation uses duk::is and then duk::get. + * + * \code + * duk::optional<int>(ctx, -1, 123); // 123 is -1 has no value or is not an int + * \endcode + */ + +/** + * \page js-more Extensions and advanced features. + * \brief Evolved extensions provided by libjs. + * + * The following topics are provided by libjs and have no equivalent in Duktape C API. + * + * \subpage js-more-t1 + */ + +/** + * \page js-more-t1 Advanced 1: adding your own types to TypeTraits + * \brief How to specialize duk::TypeTraits structure. + * + * This topic shows how you can specialize the duk::TypeTraits structure to add more types. + * + * Specializing the duk::TypeTraits is usually only needed when you want to convert a C++ object into JavaScript. + * + * In this example we will convert a C++ small structure containing two integers to JavaScript. + * + * \note It is not required to implement all functions from duk::TypeTraits. Just provide which one you need. + * + * ## The C++ structure + * + * The point does not have any methods, it just a description of two integers. + * + * \code + * struct Point { + * int x; + * int y; + * }; + * \endcode + * + * ## The push function + * + * Let's add a push function that will create a JavaScript object with **x** and **y** properties. + * + * \code + * namespace duk { + * + * template <> + * class TypeTraits<Point> { + * public: + * static void push(Context *ctx, const Point &point) + * { + * // Create an object + * push(ctx, Object()); + * + * // Set x + * putProperty(ctx, -1, "x", point.x); + * + * // Set y + * putProperty(ctx, -1, "y", point.y); + * } + * }; + * + * } + * \endcode + * + * You can safely use different type of reference as second argument. + * + * That's it, you can now safely invoke `duk::push(ctx, Point{100, 200});`. + * + * ## The get function + * + * The get function usually return a new object. The recommandation is to provide sane defaults except if you have any + * reason to not do so. + * + * \code + * namespace duk { + * + * template <> + * class TypeTraits<Point> { + * public: + * static Point get(Context *ctx, Index index) + * { + * Point point{0, 0}; + * + * if (is<Object>(ctx, index)) { + * point.x = getProperty<int>(ctx, index, "x"); + * point.y = getProperty<int>(ctx, index, "y"); + * } + * + * return point; + * } + * }; + * + * } + * \endcode + * + * Now you can invoke `duk::get<Point>(ctx, 0)` to convert a JavaScript object to the Point structure. + * + * ## The require function + * + * The require function has the same signature as get. It's up to you to decide which criterias makes the object not + * suitable for conversion. + * + * In that example, we will require that object at the index is a JavaScript object and that **x**, **y** are present. + * + * \code + * namespace duk { + * + * template <> + * class TypeTraits<Point> { + * public: + * static Point require(Context *ctx, Index index) + * { + * Point point; + * + * // Raise an error if not object + * if (!is<Object>(ctx, index)) + * duk::raise(ctx, TypeError("object required")); + * + * // Get x, for simplicity we just check that x and y exist. + * if (!hasProperty(ctx, index, "x")) + * duk::raise(ctx, TypeError("property x missing")); + * + * // Get y + * if (!hasProperty(ctx, index, "y")) + * duk::raise(ctx, TypeError("property y missing")); + * + * // Note: if you need more security, check that types are integers too. + * point.x = duk::getProperty<int>(ctx, index, "x"); + * point.y = duk::getProperty<int>(ctx, index, "y"); + * + * return point; + * } + * }; + * + * } + * \endcode + * + * ## The is function + * + * The is function returns a boolean. Again, you decide when the value is appropriate. + * + * \code + * + * namespace duk { + * + * template <> + * class TypeTraits<Point> { + * public: + * static bool is(Context *ctx, Index index) + * { + * return is<Object>(ctx, index) && hasProperty(ctx, index, "x") && hasProperty(ctx, index, "y"); + * } + * }; + * + * } + * + * \endcode + * + * ## The optional function + * + * The optional function is like get, you should return a value when it is appropriate for conversion. The + * recommandation is to return the default value **only if** there is no value at the given index or it it not + * the correct type. + * + * Usual implementation looks like this: + * + * \code + * namespace duk { + * + * template <> + * class TypeTraits<Point> { + * public: + * static Point optional(Context *ctx, Index index, Point def) + * { + * return is(ctx, index) ? get(ctx, index) : def; + * } + * }; + * + * } + * \endcode + */ + #include <cassert> +#include <cstdio> #include <functional> #include <memory> #include <string> @@ -47,13 +476,40 @@ */ namespace duk { -class Context; +/** + * \brief Typedef without pointer. + */ +using Context = ::duk_context; + +/** + * \brief Typedef for duk_double_t. + */ +using Double = ::duk_double_t; + +/** + * \brief Typedef for duk_idx_t. + */ +using Index = ::duk_idx_t; -using CodePoint = duk_codepoint_t; -using ContextPtr = duk_context *; -using Index = duk_idx_t; -using Ret = duk_ret_t; -using Size = duk_size_t; +/** + * \brief Typedef for duk_ret_t. + */ +using Ret = ::duk_ret_t; + +/** + * \brief Typedef for duk_size_t. + */ +using Size = ::duk_size_t; + +/** + * \brief Typedef for duk_int_t. + */ +using Int = ::duk_int_t; + +/** + * \brief Typedef for duk_uint_t; + */ +using Uint = ::duk_uint_t; /** * \class StackAssert @@ -69,7 +525,7 @@ class StackAssert { #if !defined(NDEBUG) private: - ContextPtr m_context; + Context *m_context; unsigned m_expected; unsigned m_begin; #endif @@ -83,7 +539,7 @@ * \param ctx the context * \param expected the size expected relative to the already existing values */ - inline StackAssert(ContextPtr ctx, unsigned expected = 0) noexcept + inline StackAssert(Context *ctx, unsigned expected = 0) noexcept #if !defined(NDEBUG) : m_context(ctx) , m_expected(expected) @@ -104,106 +560,129 @@ inline ~StackAssert() noexcept { #if !defined(NDEBUG) - assert((unsigned)duk_get_top(m_context) - m_begin == m_expected); + 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); + std::abort(); + } #endif } }; /** + * \class TypeTraits + * \brief Type information to implement new types in JavaScript's context. + * + * %This class depending on your needs may have the following functions: + * + * ## Construct + * + * Used by duk::construct, the function must place the value as this binding when the Duktape C function is + * new-constructed. + * + * \code + * static void construct(Context *ctx, Type value); + * \endcode + * + * ## Get + * + * Convert the value at the given index and return it. Should return default object if value is invalid. + * + * \code + * static Type get(Context *ctx, int index); + * \endcode + * + * ## Is + * + * Tells if the value at given index is of the requested type. + * + * \code + * static bool is(Context *ctx, int index); + * \endcode + * + * ## Optional + * + * Get the value at the given index or return the defaultValue. + * + * \code + * static Type optional(Context *ctx, int index, Type defaultValue); + * \endcode + * + * ## Push + * + * Push the value into the stack. + * + * \code + * static void push(Context *ctx, Type value); + * \endcode + * + * ## Put + * + * Apply the value to the object at the top of the stack. + * + * \code + * static void put(Context *ctx, Type value); + * \endcode + * + * ## Require + * + * Require a value at the given index. + * + * \code + * static Type require(Context *ctx, int index); + * \endcode + * + */ +template <typename Type> +class TypeTraits; + +/** * \class Object - * \brief Empty class tag for push() function. + * \brief Special type for duk::TypeTraits. */ class Object { }; /** * \class Array - * \brief Empty class tag for push() function. + * \brief Special type for duk::TypeTraits. */ class Array { }; /** * \class Global - * \brief Empty class tag to push the global object. + * \brief Special type for duk::TypeTraits. */ class Global { }; /** * \class Undefined - * \brief Empty class tag to push undefined to the stack. + * \brief Special type for duk::TypeTraits. */ class Undefined { }; /** * \class Null - * \brief Empty class tag to push null to the stack. + * \brief Special type for duk::TypeTraits. */ class Null { }; /** * \class This - * \brief Empty class tag to push this binding to the stack. + * \brief Special type for duk::TypeTraits. */ class This { }; /** - * \class RawPointer - * \brief Push a non-managed pointer to Duktape, the pointer will never be deleted. - * \note For a managed pointer with prototype, see Pointer - */ -template <typename T> -class RawPointer { -public: - /** - * The pointer to push. - */ - T *object; -}; - -/** - * \brief Manage shared_ptr from C++ and JavaScript - * - * This class allowed you to push and retrieve shared_ptr from C++ and JavaScript without taking care of ownership - * and deletion. - * - */ -template <typename T> -class Shared { -public: - /** - * The shared object. - */ - std::shared_ptr<T> object; -}; - -/** - * \brief Manage pointers from C++ and JavaScript - * - * This class allowed you to push and retrieve C++ pointers from C++ and JavaScript. The object will be deleted when - * the JavaScript garbage collectors collect them so never store a pointer created with this. - * - * The only requirement is to have the function `void prototype(Context &ctx)` in your class T. - */ -template <typename T> -class Pointer { -public: - /** - * The object. - */ - T *object{nullptr}; - - Pointer(T *o) - : object(o) - { - } -}; - -/** * \class Function * \brief Duktape/C function definition. * @@ -221,7 +700,13 @@ */ duk_idx_t nargs{0}; - Function(duk_c_function f, duk_idx_t n = 0) + /** + * Constructor for compatibility. + * + * \param f the function + * \param n the number of arguments + */ + inline Function(duk_c_function f, duk_idx_t n = 0) noexcept : function(f) , nargs(n) { @@ -229,23 +714,23 @@ }; /** - * Map of functions to set on an object. + * \brief Map of functions. */ using FunctionMap = std::unordered_map<std::string, Function>; /** - * Map of string to type, ideal for setting constants like enums. + * \brief Map of any type. */ -template <typename Type> -using Map = std::unordered_map<std::string, Type>; +template <typename T> +using Map = std::unordered_map<std::string, T>; /** - * \class ErrorInfo + * \class Exception * \brief Error description. * * This class fills the fields got in an Error object. */ -class ErrorInfo : public std::exception { +class Exception : public std::exception { public: std::string name; //!< name of error std::string message; //!< error message @@ -265,69 +750,35 @@ }; /** - * \class TypeTraits - * \brief Type information to implement new types in JavaScript's context. - * - * This class depending on your needs may have the following functions: - * - * - `static void construct(Context &ctx, Type value)` - * - `static Type get(Context &ctx, int index)` - * - `static bool is(Context &ctx, int index)` - * - `static Type optional(Context &ctx, int index, Type defaultValue)` - * - `static void push(Context &ctx, Type value)` - * - `static Type require(Context &ctx, int index)` - * - * The `construct` function is used in Context::construct to build a new value as this (e.g. constructors). - * - * The `get` function is used in Context::get, Context::getProperty, Context::getGlobal to retrieve a value from the - * stack. - * - * The `is` function is used in Context::is to check if the value on the stack is of type `Type`. - * - * The `optional` function is used in Context::optional to get a value or a replacement if not applicable. - * - * The `push` function is used in Context::push to usually create a new value on the stack but some specializations - * may not (e.g. FunctionMap). - * - * The `require` function is used in Context::require to get a value from the stack or raise a JavaScript exception if - * not applicable. - * - * This class is fully specialized for: `bool`, `const char *`, `double`, `int`, `std::string`. - * - * It is also partially specialized for : `Global`, `Object`, `Array`, `Undefined`, `Null`, `std::vector<Type>`. - */ -template <typename Type> -class TypeTraits { -}; - -/** - * \class Context * \brief RAII based Duktape handler. * * This class is implicitly convertible to duk_context for convenience. */ -class Context { +class UniqueContext { private: using Deleter = void (*)(duk_context *); using Handle = std::unique_ptr<duk_context, Deleter>; Handle m_handle; - Context(const Context &) = delete; - Context &operator=(const Context &) = delete; - Context(const Context &&) = delete; - Context &operator=(const Context &&) = delete; + UniqueContext(const UniqueContext &) = delete; + UniqueContext &operator=(const UniqueContext &) = delete; public: /** * Create default context. */ - inline Context() + inline UniqueContext() : m_handle(duk_create_heap_default(), duk_destroy_heap) { } /** + * Default move constructor. + */ + UniqueContext(UniqueContext &&) noexcept = default; + + /** * Convert the context to the native Duktape/C type. * * \return the duk_context @@ -346,34 +797,48 @@ { return m_handle.get(); } + + /** + * Default move assignment operator. + * + * \return this + */ + UniqueContext &operator=(Context &&) noexcept = delete; }; /** - * Get the error object when a JavaScript error has been thrown (e.g. eval failure). + * \name Duktape C functions + * \brief The following functions are wrappers on top of the Duktape C functions. + * + * They are as close as possible to the original functions. + */ + +/** + * \{ + */ + +/** + * Wrapper for [duk_allow](http://duktape.org/api.html#duk_allow). * * \param ctx the context - * \param index the index - * \return the information + * \param size the requested size + * \return the pointer */ -inline ErrorInfo error(ContextPtr ctx, int index) +inline void *alloc(Context *ctx, Size size) { - ErrorInfo error; - - index = duk_normalize_index(ctx, index); + return duk_alloc(ctx, size); +} - duk_get_prop_string(ctx, index, "name"); - error.name = duk_to_string(ctx, -1); - duk_get_prop_string(ctx, index, "message"); - error.message = duk_to_string(ctx, -1); - duk_get_prop_string(ctx, index, "fileName"); - error.fileName = duk_to_string(ctx, -1); - duk_get_prop_string(ctx, index, "lineNumber"); - error.lineNumber = duk_to_int(ctx, -1); - duk_get_prop_string(ctx, index, "stack"); - error.stack = duk_to_string(ctx, -1); - duk_pop_n(ctx, 5); - - return error; +/** + * Wrapper for [duk_allow_raw](http://duktape.org/api.html#duk_allow_raw). + * + * \param ctx the context + * \param size the requested size + * \return the pointer + */ +inline void *allocRaw(Context *ctx, Size size) +{ + return duk_alloc_raw(ctx, size); } /** @@ -382,7 +847,7 @@ * \param ctx the context * \param index the index */ -inline void base64Decode(ContextPtr ctx, Index index) +inline void base64Decode(Context *ctx, Index index) { duk_base64_decode(ctx, index); } @@ -392,10 +857,11 @@ * * \param ctx the context * \param index the index + * \return the base64 string */ -inline void base64Encode(ContextPtr ctx, Index index) +inline std::string base64Encode(Context *ctx, Index index) { - duk_base64_encode(ctx, index); + return duk_base64_encode(ctx, index); } /** @@ -404,7 +870,7 @@ * \param ctx the context * \param nargs the number of arguments */ -inline void call(ContextPtr ctx, Index nargs = 0) +inline void call(Context *ctx, Index nargs = 0) { duk_call(ctx, nargs); } @@ -415,7 +881,7 @@ * \param ctx the context * \param nargs the number of arguments */ -inline void callMethod(ContextPtr ctx, Index nargs = 0) +inline void callMethod(Context *ctx, Index nargs = 0) { duk_call_method(ctx, nargs); } @@ -427,7 +893,7 @@ * \param index the object index * \param nargs the number of arguments */ -inline void callProperty(ContextPtr ctx, Index index, Index nargs = 0) +inline void callProperty(Context *ctx, Index index, Index nargs = 0) { duk_call_prop(ctx, index, nargs); } @@ -438,8 +904,9 @@ * \param ctx the context * \param index the index * \param charOffset the offset + * \return the code point */ -inline CodePoint charCodeAt(ContextPtr ctx, Index index, Size charOffset) +inline duk_codepoint_t charCodeAt(Context *ctx, Index index, duk_size_t charOffset) { return duk_char_code_at(ctx, index, charOffset); } @@ -451,9 +918,9 @@ * \param extra the extra space * \return true if space is available */ -inline bool checkStack(ContextPtr ctx, Index extra) +inline bool checkStack(Context *ctx, Index extra) { - return duk_check_stack(ctx, extra); + return duk_check_stack(ctx, extra) != 0; } /** @@ -463,9 +930,9 @@ * \param top the extra space * \return true if space is available */ -inline bool checkStackTop(ContextPtr ctx, Index top) +inline bool checkStackTop(Context *ctx, Index top) { - return duk_check_stack_top(ctx, top); + return duk_check_stack_top(ctx, top) != 0; } /** @@ -476,9 +943,9 @@ * \param type the desired type * \return true if object is given type */ -inline bool checkType(ContextPtr ctx, Index index, int type) +inline bool checkType(Context *ctx, Index index, int type) { - return duk_check_type(ctx, index, type); + return duk_check_type(ctx, index, type) != 0; } /** @@ -489,9 +956,9 @@ * \param mask the desired mask * \return true if object is one of the type */ -inline bool checkTypeMask(ContextPtr ctx, Index index, unsigned mask) +inline bool checkTypeMask(Context *ctx, Index index, unsigned mask) { - return duk_check_type_mask(ctx, index, mask); + return duk_check_type_mask(ctx, index, mask) != 0; } /** @@ -500,7 +967,7 @@ * \param ctx the context * \param objIndex the object index */ -inline void compact(ContextPtr ctx, Index objIndex) +inline void compact(Context *ctx, Index objIndex) { duk_compact(ctx, objIndex); } @@ -511,7 +978,7 @@ * \param ctx the context * \param count the number of values */ -inline void concat(ContextPtr ctx, Index count) +inline void concat(Context *ctx, Index count) { duk_concat(ctx, count); } @@ -519,21 +986,34 @@ /** * Wrapper for [duk_copy](http://duktape.org/api.html#duk_copy). * + * \param ctx the context * \param from the from index * \param to the destination */ -inline void copy(ContextPtr ctx, Index from, Index to) +inline void copy(Context *ctx, Index from, Index to) { duk_copy(ctx, from, to); } /** + * Wrapper for [duk_new](http://duktape.org/api.html#duk_new). + * + * \param ctx the context + * \param nargs the number of arguments + */ +inline void create(Context *ctx, int nargs = 0) +{ + duk_new(ctx, nargs); +} + +/** * Wrapper for [duk_def_prop](http://duktape.org/api.html#duk_def_prop). * + * \param ctx the context * \param index the object index * \param flags the flags */ -inline void defineProperty(ContextPtr ctx, Index index, unsigned flags) +inline void defineProperty(Context *ctx, Index index, unsigned flags) { duk_def_prop(ctx, index, flags); } @@ -541,44 +1021,48 @@ /** * Wrapper for [duk_del_prop](http://duktape.org/api.html#duk_del_prop). * + * \param ctx the context * \param index the object index * \return true if deleted */ -inline bool deleteProperty(ContextPtr ctx, Index index) +inline bool deleteProperty(Context *ctx, Index index) { - return duk_del_prop(ctx, index); + return duk_del_prop(ctx, index) != 0; } /** * Wrapper for [duk_del_prop](http://duktape.org/api.html#duk_del_prop). * + * \param ctx the context * \param index the object index * \param position the property index * \return true if deleted */ -inline bool deleteProperty(ContextPtr ctx, Index index, unsigned position) +inline bool deleteProperty(Context *ctx, Index index, unsigned position) { - return duk_del_prop_index(ctx, index, position); + return duk_del_prop_index(ctx, index, position) != 0; } /** * Wrapper for [duk_del_prop](http://duktape.org/api.html#duk_del_prop). * + * \param ctx the context * \param index the object index * \param name the property name * \return true if deleted */ -inline bool deleteProperty(ContextPtr ctx, Index index, const std::string &name) +inline bool deleteProperty(Context *ctx, Index index, const std::string &name) { - return duk_del_prop_string(ctx, index, name.c_str()); + return duk_del_prop_string(ctx, index, name.c_str()) != 0; } /** * Wrapper for [duk_dup](http://duktape.org/api.html#duk_dup). * + * \param ctx the context * \param index the value to copy */ -inline void dup(ContextPtr ctx, int index = -1) +inline void dup(Context *ctx, int index = -1) { duk_dup(ctx, index); } @@ -591,9 +1075,9 @@ * \param index2 the second value * \return true if they equal */ -inline bool equals(ContextPtr ctx, Index index1, Index index2) +inline bool equals(Context *ctx, Index index1, Index index2) { - return duk_equals(ctx, index1, index2); + return duk_equals(ctx, index1, index2) != 0; } /** @@ -601,7 +1085,7 @@ * * \param ctx the context */ -inline void eval(ContextPtr ctx) +inline void eval(Context *ctx) { duk_eval(ctx); } @@ -613,12 +1097,9 @@ * \param path the path * \param result true to get the result at the top of the stack */ -inline void evalFile(ContextPtr ctx, const std::string &path, bool result = true) +inline void evalFile(Context *ctx, const std::string &path, bool result = true) { - if (result) - duk_eval_file(ctx, path.c_str()); - else - duk_eval_file_noresult(ctx, path.c_str()); + return result ? duk_eval_file(ctx, path.c_str()) : duk_eval_file_noresult(ctx, path.c_str()); } /** @@ -628,12 +1109,9 @@ * \param src the source script * \param result true to get the result at the top of the stack */ -inline void evalString(ContextPtr ctx, const std::string &src, bool result = true) +inline void evalString(Context *ctx, const std::string &src, bool result = true) { - if (result) - duk_eval_string(ctx, src.c_str()); - else - duk_eval_string_noresult(ctx, src.c_str()); + return result ? duk_eval_string(ctx, src.c_str()) : duk_eval_string_noresult(ctx, src.c_str()); } /** * Wrapper for [duk_gc](http://duktape.org/api.html#duk_gc). @@ -641,7 +1119,7 @@ * \param ctx the context * \param flags the flags */ -inline void gc(ContextPtr ctx, unsigned flags = 0) +inline void gc(Context *ctx, unsigned flags = 0) { duk_gc(ctx, flags); } @@ -653,9 +1131,9 @@ * \param index the object index * \return true if has */ -inline bool hasProperty(ContextPtr ctx, Index index) +inline bool hasProperty(Context *ctx, Index index) { - return duk_has_prop(ctx, index); + return duk_has_prop(ctx, index) != 0; } /** @@ -666,9 +1144,9 @@ * \param position the property index * \return true if has */ -inline bool hasProperty(ContextPtr ctx, Index index, unsigned position) +inline bool hasProperty(Context *ctx, Index index, unsigned position) { - return duk_has_prop_index(ctx, index, position); + return duk_has_prop_index(ctx, index, position) != 0; } /** @@ -679,9 +1157,9 @@ * \param name the property name * \return true if has */ -inline bool hasProperty(ContextPtr ctx, int index, const std::string &name) +inline bool hasProperty(Context *ctx, int index, const std::string &name) { - return duk_has_prop_string(ctx, index, name.c_str()); + return duk_has_prop_string(ctx, index, name.c_str()) != 0; } /** @@ -691,7 +1169,7 @@ * \param to the destination * \note Wrapper of duk_insert */ -inline void insert(ContextPtr ctx, Index to) +inline void insert(Context *ctx, Index to) { duk_insert(ctx, to); } @@ -704,9 +1182,20 @@ * \param idx2 the instance requested * \return true if idx1 is instance of idx2 */ -inline bool instanceof(ContextPtr ctx, Index idx1, Index idx2) +inline bool instanceof(Context *ctx, Index idx1, Index idx2) { - return duk_instanceof(ctx, idx1, idx2); + return duk_instanceof(ctx, idx1, idx2) != 0; +} + +/** + * Wrapper for [duk_is_constructor_call](http://duktape.org/api.html#duk_is_constructor_call). + * + * \param ctx the context + * \return true if it's a constructor call (new operator) + */ +inline bool isConstructorCall(Context *ctx) +{ + return duk_is_constructor_call(ctx) != 0; } /** @@ -715,7 +1204,7 @@ * \param ctx the context * \param count the number of values */ -inline void join(ContextPtr ctx, Index count) +inline void join(Context *ctx, Index count) { duk_join(ctx, count); } @@ -726,7 +1215,7 @@ * \param ctx the context * \param index the index */ -inline void jsonDecode(ContextPtr ctx, Index index) +inline void jsonDecode(Context *ctx, Index index) { duk_json_decode(ctx, index); } @@ -736,10 +1225,11 @@ * * \param ctx the context * \param index the index + * \return the JSON string */ -inline void jsonEncode(ContextPtr ctx, Index index) +inline std::string jsonEncode(Context *ctx, Index index) { - duk_json_encode(ctx, index); + return duk_json_encode(ctx, index); } /** @@ -747,8 +1237,9 @@ * * \param ctx the context * \param index the index + * \return the absolute index */ -inline Index normalizeIndex(ContextPtr ctx, Index index) +inline Index normalizeIndex(Context *ctx, Index index) { return duk_normalize_index(ctx, index); } @@ -758,18 +1249,45 @@ * * \param ctx the context * \param nargs the number of arguments + * \return non zero on failure */ -inline int pcall(ContextPtr ctx, Index nargs = 0) +inline int pcall(Context *ctx, Index nargs = 0) { return duk_pcall(ctx, nargs); } /** + * Wrapper for [duk_pcall_method](http://duktape.org/api.html#duk_pcall_method). + * + * \param ctx the context + * \param nargs the number of arguments + * \return non zero on failure + */ +inline int pcallMethod(Context *ctx, Index nargs = 0) +{ + return duk_pcall_method(ctx, nargs); +} + +/** + * Wrapper for [duk_pcall_prop](http://duktape.org/api.html#duk_pcall_prop). + * + * \param ctx the context + * \param index the object index + * \param nargs the number of arguments + * \return non zero on failure + */ +inline int pcallProperty(Context *ctx, Index index, Index nargs = 0) +{ + return duk_pcall_prop(ctx, index, nargs); +} + +/** * Wrapper for [duk_peval](http://duktape.org/api.html#duk_peval). * * \param ctx the context + * \return non zero on failure */ -inline int peval(ContextPtr ctx) +inline int peval(Context *ctx) { return duk_peval(ctx); } @@ -780,8 +1298,9 @@ * \param ctx the context * \param path the path * \param result true to get the result at the top of the stack + * \return non zero on failure */ -inline int pevalFile(ContextPtr ctx, const std::string &path, bool result = true) +inline int pevalFile(Context *ctx, const std::string &path, bool result = true) { return result ? duk_peval_file(ctx, path.c_str()) : duk_peval_file_noresult(ctx, path.c_str()); } @@ -792,8 +1311,9 @@ * \param ctx the context * \param src the source script * \param result true to get the result at the top of the stack + * \return non zero on failure */ -inline int pevalString(ContextPtr ctx, const std::string &src, bool result = true) +inline int pevalString(Context *ctx, const std::string &src, bool result = true) { return result ? duk_peval_string(ctx, src.c_str()) : duk_peval_string_noresult(ctx, src.c_str()); } @@ -804,18 +1324,53 @@ * \param ctx the context * \param count the number of values to pop */ -inline void pop(ContextPtr ctx, Index count = 1) +inline void pop(Context *ctx, Index count = 1) { duk_pop_n(ctx, count); } /** + * Wrapper for [duk_put_prop](http://duktape.org/api.html#duk_put_prop). + * + * \param ctx the context + * \param index the object index + */ +inline void putProperty(Context *ctx, Index index) +{ + duk_put_prop(ctx, index); +} + +/** + * Wrapper for [duk_put_prop_string](http://duktape.org/api.html#duk_put_prop_string). + * + * \param ctx the context + * \param index the object index + * \param name the property name + */ +inline void putProperty(Context *ctx, Index index, const std::string &name) +{ + duk_put_prop_string(ctx, index, name.c_str()); +} + +/** + * Wrapper for [duk_put_prop_index](http://duktape.org/api.html#duk_put_prop_index). + * + * \param ctx the context + * \param index the object index + * \param position the array position + */ +inline void putProperty(Context *ctx, Index index, unsigned position) +{ + duk_put_prop_index(ctx, index, position); +} + +/** * Wrapper for [duk_remove](http://duktape.org/api.html#duk_remove). * * \param ctx the context * \param index the value to remove */ -inline void remove(ContextPtr ctx, Index index) +inline void remove(Context *ctx, Index index) { duk_remove(ctx, index); } @@ -826,7 +1381,7 @@ * \param ctx the context * \param index the value to replace by the value at the top of the stack */ -inline void replace(ContextPtr ctx, Index index) +inline void replace(Context *ctx, Index index) { duk_replace(ctx, index); } @@ -835,9 +1390,9 @@ * Wrapper for [duk_set_finalizer](http://duktape.org/api.html#duk_set_finalizer). * * \param ctx the context - * \param index the object index + * \param index the value index */ -inline void setFinalizer(ContextPtr ctx, Index index) +inline void setFinalizer(Context *ctx, Index index) { duk_set_finalizer(ctx, index); } @@ -848,7 +1403,7 @@ * \param ctx the context * \param index the value index */ -inline void setPrototype(ContextPtr ctx, Index index) +inline void setPrototype(Context *ctx, Index index) { duk_set_prototype(ctx, index); } @@ -860,7 +1415,7 @@ * \param index1 the first index * \param index2 the second index */ -inline void swap(ContextPtr ctx, Index index1, Index index2) +inline void swap(Context *ctx, Index index1, Index index2) { duk_swap(ctx, index1, index2); } @@ -871,7 +1426,7 @@ * \param ctx the context * \param index the index */ -inline void swapTop(ContextPtr ctx, Index index) +inline void swapTop(Context *ctx, Index index) { duk_swap_top(ctx, index); } @@ -882,50 +1437,127 @@ * \param ctx the context * \return the stack size */ -inline int top(ContextPtr ctx) noexcept +inline int top(Context *ctx) { return duk_get_top(ctx); } /** + * Wrapper for [duk_throw](http://duktape.org/api.html#duk_throw). + * + * \param ctx the context + */ +inline void raise(Context *ctx) +{ + duk_throw(ctx); +} + +/** + *Wrapper for [duk_error](http://duktape.org/api.html#duk_error). + * + * \param ctx the context + * \param type the error type (e.g. DUK_ERR_REFERENCE_ERROR) + * \param fmt the format string + * \param args the arguments + */ +template <typename... Args> +inline void raise(Context *ctx, int type, const char *fmt, Args&&... args) +{ + duk_error(ctx, type, fmt, std::forward<Args>(args)...); +} + +/** * Wrapper for [duk_get_type](http://duktape.org/api.html#duk_get_type). * * \param ctx the context - * \param index the idnex + * \param index the index * \return the type */ -inline int type(ContextPtr ctx, Index index) noexcept +inline int type(Context *ctx, Index index) { return duk_get_type(ctx, index); } -/* - * Push / Get / Require / Is / Optional - * ---------------------------------------------------------- +/** + * \} + */ + +/** + * \name Extended functions + * \brief Extended functions for libjs. * - * The following functions are used to push, get or check values from the stack. They use specialization - * of TypeTraits class. + * The following functions are largely modified or extensions to Duktape. + */ + +/** + * \{ */ /** - * Push a value into the stack. Calls TypeTraits<T>::push(*this, value); + * 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 exception(Context *ctx, int index, bool pop = true) +{ + Exception ex; + + index = duk_normalize_index(ctx, index); + + duk_get_prop_string(ctx, index, "name"); + ex.name = duk_to_string(ctx, -1); + 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); + duk_get_prop_string(ctx, index, "lineNumber"); + ex.lineNumber = duk_to_int(ctx, -1); + duk_get_prop_string(ctx, index, "stack"); + ex.stack = duk_to_string(ctx, -1); + duk_pop_n(ctx, 5); + + if (pop) + duk_remove(ctx, index); + + return ex; +} + +/** + * Push a value into the stack. Calls TypeTraits<T>::push(ctx, value); + * + * \param ctx the context * \param value the value to forward */ template <typename Type> -inline void push(ContextPtr ctx, Type &&value) +inline void push(Context *ctx, Type &&value) { TypeTraits<std::decay_t<Type>>::push(ctx, std::forward<Type>(value)); } /** + * Put the value to the object at the top of the stack. Calls TypeTraits<T>::put(ctx, value); + * + * \param ctx the context + * \param value the value to apply + */ +template <typename Type> +inline void put(Context *ctx, Type &&value) +{ + TypeTraits<std::decay_t<Type>>::put(ctx, std::forward<Type>(value)); +} + +/** * Generic template function to get a value from the stack. * + * \param ctx the context * \param index the index * \return the value */ template <typename Type> -inline auto get(ContextPtr ctx, int index) -> decltype(TypeTraits<Type>::get(ctx, 0)) +inline auto get(Context *ctx, int index) -> decltype(TypeTraits<Type>::get(ctx, 0)) { return TypeTraits<Type>::get(ctx, index); } @@ -933,11 +1565,12 @@ /** * Require a type at the specified index. * + * \param ctx the context * \param index the index * \return the value */ template <typename Type> -inline auto require(ContextPtr ctx, int index) -> decltype(TypeTraits<Type>::require(ctx, 0)) +inline auto require(Context *ctx, int index) -> decltype(TypeTraits<Type>::require(ctx, 0)) { return TypeTraits<Type>::require(ctx, index); } @@ -947,11 +1580,12 @@ * * The TypeTraits<T> must have `static bool is(ContextPtr ptr, int index)`. * + * \param ctx the context * \param index the value index * \return true if is the type */ template <typename T> -inline bool is(ContextPtr ctx, int index) +inline bool is(Context *ctx, int index) { return TypeTraits<T>::is(ctx, index); } @@ -962,33 +1596,28 @@ * * The TypeTraits<T> must have `static T optional(Context &, int index, T &&defaultValue)`. * + * \param ctx the context * \param index the value index * \param defaultValue the value replacement * \return the value or defaultValue */ template <typename Type> -inline auto optional(ContextPtr ctx, int index, Type &&defaultValue) +inline auto optional(Context *ctx, int index, Type &&defaultValue) { return TypeTraits<std::decay_t<Type>>::optional(ctx, index, std::forward<Type>(defaultValue)); } -/* - * Properties management - * ---------------------------------------------------------- - * - * The following functions are used to read or set properties on objects or globals also using TypeTraits. - */ - /** * Get the property `name' as value from the object at the specified index. * + * \param ctx the context * \param index the object index * \param name the property name * \return the value * \note The stack is unchanged */ template <typename Type, typename std::enable_if_t<!std::is_void<Type>::value> * = nullptr> -inline auto getProperty(ContextPtr ctx, int index, const std::string &name) -> decltype(get<Type>(ctx, 0)) +inline auto getProperty(Context *ctx, int index, const std::string &name) -> decltype(get<Type>(ctx, 0)) { duk_get_prop_string(ctx, index, name.c_str()); decltype(get<Type>(ctx, 0)) value = get<Type>(ctx, -1); @@ -1000,13 +1629,14 @@ /** * Get a property by index, for arrays. * + * \param ctx the context * \param index the object index * \param position the position int the object * \return the value * \note The stack is unchanged */ template <typename Type, typename std::enable_if_t<!std::is_void<Type>::value> * = nullptr> -inline auto getProperty(ContextPtr ctx, int index, int position) -> decltype(get<Type>(ctx, 0)) +inline auto getProperty(Context *ctx, int index, int position) -> decltype(get<Type>(ctx, 0)) { duk_get_prop_index(ctx, index, position); decltype(get<Type>(ctx, 0)) value = get<Type>(ctx, -1); @@ -1018,12 +1648,13 @@ /** * Get the property `name' and push it to the stack from the object at the specified index. * + * \param ctx the context * \param index the object index * \param name the property name * \note The stack contains the property value */ template <typename Type, typename std::enable_if_t<std::is_void<Type>::value> * = nullptr> -inline void getProperty(ContextPtr ctx, int index, const std::string &name) +inline void getProperty(Context *ctx, int index, const std::string &name) { duk_get_prop_string(ctx, index, name.c_str()); } @@ -1031,12 +1662,13 @@ /** * Get the property by index and push it to the stack from the object at the specified index. * + * \param ctx the context * \param index the object index * \param position the position in the object * \note The stack contains the property value */ template <typename Type, typename std::enable_if_t<std::is_void<Type>::value> * = nullptr> -inline void getProperty(ContextPtr ctx, int index, int position) +inline void getProperty(Context *ctx, int index, int position) { duk_get_prop_index(ctx, index, position); } @@ -1044,6 +1676,7 @@ /** * Get an optional property `name` from the object at the specified index. * + * \param ctx the context * \param index the object index * \param name the property name * \param def the default value @@ -1051,7 +1684,7 @@ * \note The stack is unchanged */ template <typename Type, typename DefaultValue> -inline auto optionalProperty(ContextPtr ctx, int index, const std::string &name, DefaultValue &&def) -> decltype(optional(ctx, 0, std::forward<DefaultValue>(def))) +inline auto optionalProperty(Context *ctx, int index, const std::string &name, DefaultValue &&def) -> decltype(optional(ctx, 0, std::forward<DefaultValue>(def))) { duk_get_prop_string(ctx, index, name.c_str()); decltype(optional(ctx, 0, std::forward<DefaultValue>(def))) value = optional(ctx, -1, std::forward<DefaultValue>(def)); @@ -1063,6 +1696,7 @@ /** * Get an optional property by index, for arrays * + * \param ctx the context * \param index the object index * \param position the position int the object * \param def the default value @@ -1070,7 +1704,7 @@ * \note The stack is unchanged */ template <typename Type, typename DefaultValue> -inline auto optionalProperty(ContextPtr ctx, int index, int position, DefaultValue &&def) -> decltype(optional(ctx, 0, std::forward<DefaultValue>(def))) +inline auto optionalProperty(Context *ctx, int index, int position, DefaultValue &&def) -> decltype(optional(ctx, 0, std::forward<DefaultValue>(def))) { duk_get_prop_index(ctx, index, position); decltype(optional(ctx, 0, std::forward<DefaultValue>(def))) value = optional(ctx, -1, std::forward<DefaultValue>(def)); @@ -1082,13 +1716,14 @@ /** * Set a property to the object at the specified index. * + * \param ctx the context * \param index the object index * \param name the property name * \param value the value to forward * \note The stack is unchanged */ template <typename Type> -void putProperty(ContextPtr ctx, int index, const std::string &name, Type &&value) +void putProperty(Context *ctx, int index, const std::string &name, Type &&value) { index = duk_normalize_index(ctx, index); @@ -1099,13 +1734,14 @@ /** * Set a property by index, for arrays. * + * \param ctx the context * \param index the object index * \param position the position in the object * \param value the value to forward * \note The stack is unchanged */ template <typename Type> -void putProperty(ContextPtr ctx, int index, int position, Type &&value) +void putProperty(Context *ctx, int index, int position, Type &&value) { index = duk_normalize_index(ctx, index); @@ -1114,35 +1750,14 @@ } /** - * Put the value that is at the top of the stack as property to the object. - * - * \param index the object index - * \param name the property name - */ -inline void putProperty(ContextPtr ctx, int index, const std::string &name) -{ - duk_put_prop_string(ctx, index, name.c_str()); -} - -/** - * Put the value that is at the top of the stack to the object as index. - * - * \param index the object index - * \param position the position in the object - */ -inline void putProperty(ContextPtr ctx, int index, int position) -{ - duk_put_prop_index(ctx, index, position); -} - -/** * Get a global value. * + * \param ctx the context * \param name the name of the global variable * \return the value */ template <typename Type> -inline auto getGlobal(ContextPtr ctx, const std::string &name, std::enable_if_t<!std::is_void<Type>::value> * = nullptr) -> decltype(get<Type>(ctx, 0)) +inline auto getGlobal(Context *ctx, const std::string &name, std::enable_if_t<!std::is_void<Type>::value> * = nullptr) -> decltype(get<Type>(ctx, 0)) { duk_get_global_string(ctx, name.c_str()); decltype(get<Type>(ctx, 0)) value = get<Type>(ctx, -1); @@ -1153,9 +1768,12 @@ /** * Overload that push the value at the top of the stack instead of returning it. + * + * \param ctx the context + * \param name the name of the global variable */ template <typename Type> -inline void getGlobal(ContextPtr ctx, const std::string &name, std::enable_if_t<std::is_void<Type>::value> * = nullptr) noexcept +inline void getGlobal(Context *ctx, const std::string &name, std::enable_if_t<std::is_void<Type>::value> * = nullptr) { duk_get_global_string(ctx, name.c_str()); } @@ -1163,11 +1781,12 @@ /** * Set a global variable. * + * \param ctx the context * \param name the name of the global variable * \param type the value to set */ template <typename Type> -inline void putGlobal(ContextPtr ctx, const std::string &name, Type&& type) +inline void putGlobal(Context *ctx, const std::string &name, Type&& type) { push(ctx, std::forward<Type>(type)); duk_put_global_string(ctx, name.c_str()); @@ -1176,30 +1795,25 @@ /** * Put the value at the top of the stack as global property. * + * \param ctx the context * \param name the property name */ -inline void putGlobal(ContextPtr ctx, const std::string &name) +inline void putGlobal(Context *ctx, const std::string &name) { duk_put_global_string(ctx, name.c_str()); } -/* - * Extra functions - * ---------------------------------------------------------- - * - * The following functions are implemented for convenience and do not exists in the native Duktape API. - */ - /** * Enumerate an object or an array at the specified index. * + * \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 */ template <typename Func> -void enumerate(ContextPtr ctx, int index, duk_uint_t flags, duk_bool_t getvalue, Func &&func) +void enumerate(Context *ctx, int index, duk_uint_t flags, duk_bool_t getvalue, Func &&func) { duk_enum(ctx, index, flags); @@ -1214,13 +1828,14 @@ /** * Return the this binding of the current function. * + * \param ctx the context * \return the this binding as the template given */ template <typename T> -inline auto self(ContextPtr ctx) -> decltype(TypeTraits<T>::get(ctx, 0)) +inline auto self(Context *ctx) -> decltype(TypeTraits<T>::require(ctx, 0)) { duk_push_this(ctx); - decltype(TypeTraits<T>::get(ctx, 0)) value = TypeTraits<T>::get(ctx, -1); + decltype(TypeTraits<T>::require(ctx, 0)) value = TypeTraits<T>::require(ctx, -1); duk_pop(ctx); return value; @@ -1229,123 +1844,35 @@ /** * Throw an ECMAScript exception. * + * \param ctx the context * \param ex the exception */ template <typename Exception> -void raise(ContextPtr ctx, const Exception &ex) +void raise(Context *ctx, const Exception &ex) { ex.raise(ctx); } /** - * Wrapper for duk_throw. - * - * \param ctx the context - */ -inline void raise(ContextPtr ctx) -{ - duk_throw(ctx); -} - -/** - * Wrapper for duk_error. - * - * \param ctx the context - * \param type the error type (e.g. DUK_ERR_REFERENCE_ERROR) - * \param fmt the format string - * \param args the arguments - */ -template <typename... Args> -inline void raise(ContextPtr ctx, int type, const char *fmt, Args&&... args) -{ - duk_error(ctx, type, fmt, std::forward<Args>(args)...); -} - -/** - * Wrapper for duk_new. - * - * \param ctx the context - * \param nargs the number of arguments - */ -inline void create(ContextPtr ctx, int nargs = 0) -{ - duk_new(ctx, nargs); -} - -/** * Construct the object in place, setting value as this binding. * * The TypeTraits<T> must have the following requirements: * * - static void construct(Context &, T): must update this with the value and keep the stack unchanged * + * \param ctx the context * \param value the value to forward * \see self */ template <typename T> -inline void construct(ContextPtr ctx, T &&value) +inline void construct(Context *ctx, T &&value) { TypeTraits<std::decay_t<T>>::construct(ctx, std::forward<T>(value)); } /** - * Sign the given object with the name from T. - * - * This is automatically done for when constructing/pushing object with Shared and Pointer helpers, however you need - * to manually add it when using inheritance. + * \} */ -template <typename T> -inline void sign(ContextPtr ctx, Index index) -{ - StackAssert sa(ctx, 0); - - index = duk_normalize_index(ctx, index); - - duk_push_string(ctx, TypeTraits<T>::name().c_str()); - duk_push_boolean(ctx, true); - duk_def_prop(ctx, index < 0 ? index : index, DUK_DEFPROP_HAVE_VALUE); - - /* Do for inherited classes */ - for (const std::string &parent : TypeTraits<T>::inherits()) { - duk_push_string(ctx, parent.c_str()); - duk_push_boolean(ctx, true); - duk_def_prop(ctx, index < 0 ? index : index, DUK_DEFPROP_HAVE_VALUE); - } -} - -/** - * Check if the object at the given index is signed by T or raise TypeError if not. - * - * \param ctx the context - * \param index the index - * \see sign - */ -template <typename T> -inline void checkSignature(ContextPtr ctx, Index index) -{ - StackAssert sa(ctx, 0); - - if (!is<Object>(ctx, index) || !getProperty<bool>(ctx, index, TypeTraits<T>::name())) - raise(ctx, DUK_ERR_TYPE_ERROR, "invalid this binding"); -} - -/** - * Tells if the object at the specified index is of type T. - * - * \param ctx the context - * \param index the index - */ -template <typename T> -inline bool isSigned(ContextPtr ctx, Index index) -{ - StackAssert sa(ctx, 0); - - return is<Object>(ctx, index) && getProperty<bool>(ctx, index, TypeTraits<T>::name()); -} - -/* ------------------------------------------------------------------ - * Exception handling - * ------------------------------------------------------------------ */ /** * \class Error @@ -1387,7 +1914,7 @@ * \note the default implementation search for the global variables * \param ctx the context */ - virtual void raise(ContextPtr ctx) const noexcept + virtual void raise(Context *ctx) const { duk_error(ctx, m_type, "%s", m_message.c_str()); } @@ -1495,10 +2022,6 @@ } }; -/* ------------------------------------------------------------------ - * Standard overloads for TypeTraits<T> - * ------------------------------------------------------------------ */ - /** * \class TypeTraits<int> * \brief Default implementation for int. @@ -1515,7 +2038,7 @@ * \param index the index * \return the integer */ - static inline int get(ContextPtr ctx, int index) + static inline int get(Context *ctx, int index) { return duk_get_int(ctx, index); } @@ -1527,9 +2050,9 @@ * \param index the index * \return true if integer */ - static inline bool is(ContextPtr ctx, int index) + static inline bool is(Context *ctx, int index) { - return duk_is_number(ctx, index); + return duk_is_number(ctx, index) != 0; } /** @@ -1540,7 +2063,7 @@ * \param defaultValue the defaultValue * \return the integer or defaultValue */ - static inline int optional(ContextPtr ctx, int index, int defaultValue) + static inline int optional(Context *ctx, int index, int defaultValue) { return is(ctx, index) ? get(ctx, index) : defaultValue; } @@ -1551,7 +2074,7 @@ * \param ctx the context * \param value the value */ - static inline void push(ContextPtr ctx, int value) + static inline void push(Context *ctx, int value) { duk_push_int(ctx, value); } @@ -1563,7 +2086,7 @@ * \param index the index * \return the integer */ - static inline int require(ContextPtr ctx, int index) + static inline int require(Context *ctx, int index) { return duk_require_int(ctx, index); } @@ -1585,9 +2108,9 @@ * \param index the index * \return the boolean */ - static inline bool get(ContextPtr ctx, int index) + static inline bool get(Context *ctx, int index) { - return duk_get_boolean(ctx, index); + return duk_get_boolean(ctx, index) != 0; } /** @@ -1597,9 +2120,9 @@ * \param index the index * \return true if boolean */ - static inline bool is(ContextPtr ctx, int index) + static inline bool is(Context *ctx, int index) { - return duk_is_boolean(ctx, index); + return duk_is_boolean(ctx, index) != 0; } /** @@ -1610,7 +2133,7 @@ * \param defaultValue the defaultValue * \return the boolean or defaultValue */ - static inline bool optional(ContextPtr ctx, int index, bool defaultValue) + static inline bool optional(Context *ctx, int index, bool defaultValue) { return is(ctx, index) ? get(ctx, index) : defaultValue; } @@ -1621,7 +2144,7 @@ * \param ctx the context * \param value the value */ - static inline void push(ContextPtr ctx, bool value) + static inline void push(Context *ctx, bool value) { duk_push_boolean(ctx, value); } @@ -1633,9 +2156,9 @@ * \param index the index * \return the boolean */ - static inline bool require(ContextPtr ctx, int index) + static inline bool require(Context *ctx, int index) { - return duk_require_boolean(ctx, index); + return duk_require_boolean(ctx, index) != 0; } }; @@ -1655,7 +2178,7 @@ * \param index the index * \return the double */ - static inline double get(ContextPtr ctx, int index) + static inline double get(Context *ctx, int index) { return duk_get_number(ctx, index); } @@ -1667,9 +2190,9 @@ * \param index the index * \return true if double */ - static inline bool is(ContextPtr ctx, int index) + static inline bool is(Context *ctx, int index) { - return duk_is_number(ctx, index); + return duk_is_number(ctx, index) != 0; } /** @@ -1680,7 +2203,7 @@ * \param defaultValue the defaultValue * \return the double or defaultValue */ - static inline double optional(ContextPtr ctx, int index, double defaultValue) + static inline double optional(Context *ctx, int index, double defaultValue) { return is(ctx, index) ? get(ctx, index) : defaultValue; } @@ -1691,7 +2214,7 @@ * \param ctx the context * \param value the value */ - static inline void push(ContextPtr ctx, double value) + static inline void push(Context *ctx, double value) { duk_push_number(ctx, value); } @@ -1703,7 +2226,7 @@ * \param index the index * \return the double */ - static inline double require(ContextPtr ctx, int index) + static inline double require(Context *ctx, int index) { return duk_require_number(ctx, index); } @@ -1727,7 +2250,7 @@ * \param index the index * \return the string */ - static inline std::string get(ContextPtr ctx, int index) + static inline std::string get(Context *ctx, int index) { duk_size_t size; const char *text = duk_get_lstring(ctx, index, &size); @@ -1742,9 +2265,9 @@ * \param index the index * \return true if string */ - static inline bool is(ContextPtr ctx, int index) + static inline bool is(Context *ctx, int index) { - return duk_is_string(ctx, index); + return duk_is_string(ctx, index) != 0; } /** @@ -1755,7 +2278,7 @@ * \param defaultValue the defaultValue * \return the string or defaultValue */ - static inline std::string optional(ContextPtr ctx, int index, std::string defaultValue) + static inline std::string optional(Context *ctx, int index, std::string defaultValue) { return is(ctx, index) ? get(ctx, index) : defaultValue; } @@ -1766,7 +2289,7 @@ * \param ctx the context * \param value the value */ - static inline void push(ContextPtr ctx, const std::string &value) + static inline void push(Context *ctx, const std::string &value) { duk_push_lstring(ctx, value.c_str(), value.length()); } @@ -1778,7 +2301,7 @@ * \param index the index * \return the string */ - static inline std::string require(ContextPtr ctx, int index) + static inline std::string require(Context *ctx, int index) { duk_size_t size; const char *text = duk_require_lstring(ctx, index, &size); @@ -1803,7 +2326,7 @@ * \param index the index * \return the string */ - static inline const char *get(ContextPtr ctx, int index) + static inline const char *get(Context *ctx, int index) { return duk_get_string(ctx, index); } @@ -1815,9 +2338,9 @@ * \param index the index * \return true if string */ - static inline bool is(ContextPtr ctx, int index) + static inline bool is(Context *ctx, int index) { - return duk_is_string(ctx, index); + return duk_is_string(ctx, index) != 0; } /** @@ -1828,7 +2351,7 @@ * \param defaultValue the defaultValue * \return the integer or defaultValue */ - static inline const char *optional(ContextPtr ctx, int index, const char *defaultValue) + static inline const char *optional(Context *ctx, int index, const char *defaultValue) { return is(ctx, index) ? get(ctx, index) : defaultValue; } @@ -1839,7 +2362,7 @@ * \param ctx the context * \param value the value */ - static inline void push(ContextPtr ctx, const char *value) + static inline void push(Context *ctx, const char *value) { duk_push_string(ctx, value); } @@ -1851,19 +2374,89 @@ * \param index the index * \return the string */ - static inline const char *require(ContextPtr ctx, int index) + static inline const char *require(Context *ctx, int index) { return duk_require_string(ctx, index); } }; /** + * \class TypeTraits<unsigned> + * \brief Default implementation for unsigned. + * + * Provides: get, is, optional, push, require. + */ +template <> +class TypeTraits<unsigned> { +public: + /** + * Get an integer, return 0 if not an integer. + * + * \param ctx the context + * \param index the index + * \return the integer + */ + static inline unsigned get(Context *ctx, int index) + { + return duk_get_uint(ctx, index); + } + + /** + * Check if value is an integer. + * + * \param ctx the context + * \param index the index + * \return true if integer + */ + static inline bool is(Context *ctx, int index) + { + return duk_is_number(ctx, index) != 0; + } + + /** + * Get an integer, return defaultValue if the value is not an integer. + * + * \param ctx the context + * \param index the index + * \param defaultValue the defaultValue + * \return the integer or defaultValue + */ + static inline unsigned optional(Context *ctx, int index, unsigned defaultValue) + { + return is(ctx, index) ? get(ctx, index) : defaultValue; + } + + /** + * Push an integer. + * + * \param ctx the context + * \param value the value + */ + static inline void push(Context *ctx, unsigned value) + { + duk_push_uint(ctx, value); + } + + /** + * Require an integer, throws a JavaScript exception if not an integer. + * + * \param ctx the context + * \param index the index + * \return the integer + */ + static inline unsigned require(Context *ctx, int index) + { + return duk_require_uint(ctx, index); + } +}; + +/** * \brief Implementation for non-managed pointers. * * Provides: get, is, optional, push, require. */ template <typename T> -class TypeTraits<RawPointer<T>> { +class TypeTraits<T *> { public: /** * Get a pointer, return nullptr if not a pointer. @@ -1872,7 +2465,7 @@ * \param index the index * \return the pointer */ - static inline T *get(ContextPtr ctx, int index) + static inline T *get(Context *ctx, int index) { return static_cast<T *>(duk_to_pointer(ctx, index)); } @@ -1884,9 +2477,9 @@ * \param index the index * \return true if pointer */ - static inline bool is(ContextPtr ctx, int index) + static inline bool is(Context *ctx, int index) { - return duk_is_pointer(ctx, index); + return duk_is_pointer(ctx, index) != 0; } /** @@ -1897,9 +2490,9 @@ * \param defaultValue the defaultValue * \return the pointer or defaultValue */ - static inline T *optional(ContextPtr ctx, int index, RawPointer<T> defaultValue) + static inline T *optional(Context *ctx, int index, T *defaultValue) { - return is(ctx, index) ? get(ctx, index) : defaultValue.object; + return is(ctx, index) ? get(ctx, index) : defaultValue; } /** @@ -1908,9 +2501,9 @@ * \param ctx the context * \param value the value */ - static inline void push(ContextPtr ctx, const RawPointer<T> &value) + static inline void push(Context *ctx, T *value) { - duk_push_pointer(ctx, value.object); + duk_push_pointer(ctx, value); } /** @@ -1920,7 +2513,7 @@ * \param index the index * \return the pointer */ - static inline T *require(ContextPtr ctx, int index) + static inline T *require(Context *ctx, int index) { return static_cast<T *>(duk_require_pointer(ctx, index)); } @@ -1944,9 +2537,9 @@ * \param index the value index * \return true if the value is callable */ - static bool is(ContextPtr ctx, Index index) + static bool is(Context *ctx, Index index) { - return duk_is_callable(ctx, index); + return duk_is_callable(ctx, index) != 0; } /** @@ -1956,7 +2549,7 @@ * \param ctx the context * \param fn the function */ - static void push(ContextPtr ctx, Function fn) + static void push(Context *ctx, Function fn) { duk_push_c_function(ctx, fn.function, fn.nargs); } @@ -1966,7 +2559,7 @@ * \class TypeTraits<FunctionMap> * \brief Put the functions to the object at the top of the stack. * - * Provides: push. + * Provides: put. */ template <> class TypeTraits<FunctionMap> { @@ -1977,7 +2570,7 @@ * \param ctx the context * \param map the map of function */ - static inline void push(ContextPtr ctx, const FunctionMap &map) + static void put(Context *ctx, const FunctionMap &map) { StackAssert sa(ctx, 0); @@ -2004,9 +2597,9 @@ * \param index the index * \return true if object */ - static inline bool is(ContextPtr ctx, int index) + static inline bool is(Context *ctx, int index) { - return duk_is_object(ctx, index); + return duk_is_object(ctx, index) != 0; } /** @@ -2014,7 +2607,7 @@ * * \param ctx the context */ - static inline void push(ContextPtr ctx, const Object &) + static inline void push(Context *ctx, const Object &) { duk_push_object(ctx); } @@ -2036,9 +2629,9 @@ * \param index the index * \return true if array */ - static inline bool is(ContextPtr ctx, int index) + static inline bool is(Context *ctx, int index) { - return duk_is_array(ctx, index); + return duk_is_array(ctx, index) != 0; } /** @@ -2046,7 +2639,7 @@ * * \param ctx the context */ - static inline void push(ContextPtr ctx, const Array &) + static inline void push(Context *ctx, const Array &) { duk_push_array(ctx); } @@ -2068,9 +2661,9 @@ * \param index the index * \return true if undefined */ - static inline bool is(ContextPtr ctx, int index) + static inline bool is(Context *ctx, int index) { - return duk_is_undefined(ctx, index); + return duk_is_undefined(ctx, index) != 0; } /** @@ -2078,7 +2671,7 @@ * * \param ctx the context */ - static inline void push(ContextPtr ctx, const Undefined &) + static inline void push(Context *ctx, const Undefined &) { duk_push_undefined(ctx); } @@ -2100,9 +2693,9 @@ * \param index the index * \return true if null */ - static inline bool is(ContextPtr ctx, int index) + static inline bool is(Context *ctx, int index) { - return duk_is_null(ctx, index); + return duk_is_null(ctx, index) != 0; } /** @@ -2110,7 +2703,7 @@ * * \param ctx the context */ - static inline void push(ContextPtr ctx, const Null &) + static inline void push(Context *ctx, const Null &) { duk_push_null(ctx); } @@ -2129,7 +2722,7 @@ * * \param ctx the context */ - static inline void push(ContextPtr ctx, const This &) + static inline void push(Context *ctx, const This &) { duk_push_this(ctx); } @@ -2149,7 +2742,7 @@ * * \param ctx the context */ - static inline void push(ContextPtr ctx, const Global &) + static inline void push(Context *ctx, const Global &) { duk_push_global_object(ctx); } @@ -2158,7 +2751,7 @@ /** * \brief Push a map of key-value pair as objects. * - * Provides: push. + * Provides: push, put. * * This class is convenient for settings constants such as enums, string and such. */ @@ -2172,9 +2765,26 @@ * \param map the values * \note You need an object at the top of the stack before calling this function */ - static void push(ContextPtr ctx, const std::unordered_map<std::string, T> &map) + static void push(Context *ctx, const std::unordered_map<std::string, T> &map) { - StackAssert sa(ctx, 0); + StackAssert sa(ctx, 1); + + duk_push_object(ctx); + put(ctx, map); + } + + /** + * Apply the map to the object at the top of the stack. + * + * \pre top value must be an object + * \param ctx the context + * \param map the map + */ + static void put(Context *ctx, const std::unordered_map<std::string, T> &map) + { + assert(type(ctx, -1) == DUK_TYPE_OBJECT); + + StackAssert sa(ctx); for (const auto &pair : map) { TypeTraits<T>::push(ctx, pair.second); @@ -2186,7 +2796,7 @@ /** * \brief Push or get vectors as JavaScript arrays. * - * Provides: get, push. + * Provides: get, push, put. */ template <typename T> class TypeTraits<std::vector<T>> { @@ -2198,7 +2808,7 @@ * \param index the array index * \return the array or empty array if the value is not an array */ - static std::vector<T> get(ContextPtr ctx, int index) + static std::vector<T> get(Context *ctx, int index) { StackAssert sa(ctx, 0); @@ -2207,10 +2817,10 @@ if (!duk_is_array(ctx, -1)) return result; - int total = duk_get_length(ctx, index); + size_t total = duk_get_length(ctx, index); - for (int i = 0; i < total; ++i) - result.push_back(getProperty<T>(ctx, index, i)); + for (size_t i = 0; i < total; ++i) + result.push_back(getProperty<T>(ctx, index, static_cast<int>(i))); return result; } @@ -2221,11 +2831,26 @@ * \param ctx the context * \param array the values */ - static void push(ContextPtr ctx, const std::vector<T> &array) + static void push(Context *ctx, const std::vector<T> &array) { StackAssert sa(ctx, 1); duk_push_array(ctx); + put(ctx, array); + } + + /** + * Apply the array to the object at the top of the stack. + * + * \pre top value must be an object + * \param ctx the context + * \param array the array + */ + static void put(Context *ctx, const std::vector<T> &array) + { + assert(type(ctx, -1) == DUK_TYPE_OBJECT); + + StackAssert sa(ctx); unsigned i = 0; for (const auto &v : array) { @@ -2235,167 +2860,6 @@ } }; -/** - * \brief Implementation of managed shared_ptr - * \see Shared - */ -template <typename T> -class TypeTraits<Shared<T>> { -private: - static void apply(ContextPtr ctx, std::shared_ptr<T> value) - { - StackAssert sa(ctx, 0); - - sign<T>(ctx, -1); - - duk_push_pointer(ctx, new std::shared_ptr<T>(std::move(value))); - duk_put_prop_string(ctx, -2, "\xff""\xff""js-shared-ptr"); - duk_push_c_function(ctx, [] (duk_context *ctx) -> Ret { - duk_get_prop_string(ctx, 0, "\xff""\xff""js-shared-ptr"); - delete static_cast<std::shared_ptr<T> *>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - duk_push_null(ctx); - duk_put_prop_string(ctx, 0, "\xff""\xff""js-ptr"); - - return 0; - }, 1); - duk_set_finalizer(ctx, -2); - } - -public: - /** - * Construct the shared_ptr as this. - * - * \param ctx the context - * \param value the value - */ - static void construct(ContextPtr ctx, Shared<T> value) - { - StackAssert sa(ctx, 0); - - duk_push_this(ctx); - apply(ctx, std::move(value.object)); - duk_pop(ctx); - } - - /** - * Push a managed shared_ptr as object. - * - * \param ctx the context - * \param value the value - */ - static void push(ContextPtr ctx, Shared<T> value) - { - StackAssert sa(ctx, 1); - - duk_push_object(ctx); - apply(ctx, value.object); - TypeTraits<T>::prototype(ctx); - duk_set_prototype(ctx, -2); - } - - /** - * Get a managed shared_ptr from the stack. - * - * \param ctx the context - * \param index the object index - * \return the shared_ptr - */ - static std::shared_ptr<T> get(ContextPtr ctx, int index) - { - StackAssert sa(ctx, 0); - - checkSignature<T>(ctx, index); - - duk_get_prop_string(ctx, index, "\xff""\xff""js-shared-ptr"); - std::shared_ptr<T> value = *static_cast<std::shared_ptr<T> *>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - - return value; - } -}; - -/** - * \brief Implementation of managed pointers - * \see Pointer - */ -template <typename T> -class TypeTraits<Pointer<T>> { -private: - static void apply(ContextPtr ctx, T *value) - { - StackAssert sa(ctx, 0); - - sign<T>(ctx, -1); - - duk_push_pointer(ctx, value); - duk_put_prop_string(ctx, -2, "\xff""\xff""js-ptr"); - duk_push_c_function(ctx, [] (duk_context *ctx) -> Ret { - duk_get_prop_string(ctx, 0, "\xff""\xff""js-ptr"); - delete static_cast<T *>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - duk_push_null(ctx); - duk_put_prop_string(ctx, 0, "\xff""\xff""js-ptr"); - - return 0; - }, 1); - duk_set_finalizer(ctx, -2); - } - -public: - /** - * Construct the pointer as this. - * - * \param ctx the context - * \param value the value - */ - static void construct(ContextPtr ctx, Pointer<T> value) - { - StackAssert sa(ctx, 0); - - duk_push_this(ctx); - apply(ctx, value.object); - duk_pop(ctx); - } - - /** - * Push a managed pointer as object. - * - * \param ctx the context - * \param value the value - */ - static void push(ContextPtr ctx, Pointer<T> value) - { - StackAssert sa(ctx, 1); - - duk_push_object(ctx); - apply(ctx, value.object); - TypeTraits<T>::prototype(ctx); - duk_set_prototype(ctx, -2); - } - - /** - * Get a managed pointer from the stack. - * - * \param ctx the context - * \param index the object index - * \return the pointer - * \warning Do not store the pointer into the C++ side, the object can be deleted at any time - */ - static T *get(ContextPtr ctx, int index) - { - StackAssert sa(ctx, 0); - - checkSignature<T>(ctx, index); - - duk_get_prop_string(ctx, index, "\xff""\xff""js-ptr"); - T *value = static_cast<T *>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - - return value; - } -}; - } // !duk } // !irccd
--- a/lib/irccd/mod-directory.cpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/mod-directory.cpp Tue May 31 21:03:01 2016 +0200 @@ -36,7 +36,7 @@ namespace { -std::string path(duk::ContextPtr ctx) +std::string path(duk::Context *ctx) { duk::push(ctx, duk::This{}); duk::getProperty<void>(ctx, -1, "path"); @@ -125,7 +125,7 @@ * * The patternIndex is the argument where to test if the argument is a regex or a string. */ -duk::Ret find(duk::ContextPtr ctx, std::string base, bool recursive, int patternIndex) +duk::Ret find(duk::Context *ctx, std::string base, bool recursive, int patternIndex) { base = path::clean(base); @@ -165,7 +165,7 @@ * - Directory.remove * - Directory.prototype.remove */ -duk::Ret remove(duk::ContextPtr ctx, const std::string &path, bool recursive) +duk::Ret remove(duk::Context *ctx, const std::string &path, bool recursive) { if (!fs::isDirectory(path)) duk::raise(ctx, SystemError(EINVAL, "not a directory")); @@ -197,7 +197,7 @@ * Throws: * - Any exception on error. */ -duk::Ret methodFind(duk::ContextPtr ctx) +duk::Ret methodFind(duk::Context *ctx) { return find(ctx, path(ctx), duk::optional<bool>(ctx, 1, false), 0); } @@ -214,7 +214,7 @@ * Throws: * - Any exception on error. */ -duk::Ret methodRemove(duk::ContextPtr ctx) +duk::Ret methodRemove(duk::Context *ctx) { return remove(ctx, path(ctx), duk::optional<bool>(ctx, 0, false)); } @@ -240,7 +240,7 @@ * Throws: * - Any exception on error */ -duk::Ret constructor(duk::ContextPtr ctx) +duk::Ret constructor(duk::Context *ctx) { if (!duk_is_constructor_call(ctx)) return 0; @@ -292,7 +292,7 @@ * Returns: * The path to the file or undefined on errors or not found. */ -duk::Ret funcFind(duk::ContextPtr ctx) +duk::Ret funcFind(duk::Context *ctx) { return find(ctx, duk::require<std::string>(ctx, 0), duk::optional<bool>(ctx, 2, false), 1); } @@ -309,7 +309,7 @@ * Throws: * - Any exception on error. */ -duk::Ret funcRemove(duk::ContextPtr ctx) +duk::Ret funcRemove(duk::Context *ctx) { return remove(ctx, duk::require<std::string>(ctx, 0), duk::optional<bool>(ctx, 1, false)); } @@ -327,7 +327,7 @@ * Throws: * - Any exception on error. */ -duk::Ret funcMkdir(duk::ContextPtr ctx) +duk::Ret funcMkdir(duk::Context *ctx) { try { fs::mkdir(duk::require<std::string>(ctx, 0), duk::optional<int>(ctx, 1, 0700)); @@ -366,11 +366,11 @@ duk::getGlobal<void>(plugin.context(), "Irccd"); duk::push(plugin.context(), duk::Function{constructor, 2}); - duk::push(plugin.context(), constants); - duk::push(plugin.context(), functions); + duk::put(plugin.context(), constants); + duk::put(plugin.context(), functions); duk::putProperty(plugin.context(), -1, "separator", std::string{fs::separator()}); duk::push(plugin.context(), duk::Object{}); - duk::push(plugin.context(), methods); + duk::put(plugin.context(), methods); duk::putProperty(plugin.context(), -2, "prototype"); duk::putProperty(plugin.context(), -2, "Directory"); duk::pop(plugin.context());
--- a/lib/irccd/mod-elapsed-timer.cpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/mod-elapsed-timer.cpp Tue May 31 21:03:01 2016 +0200 @@ -22,19 +22,34 @@ namespace irccd { +namespace { + +const std::string Signature{"\xff""\xff""irccd-elapsed-timer-ptr"}; + +} // !namespace + namespace duk { template <> -class TypeTraits<ElapsedTimer> { +class TypeTraits<ElapsedTimer *> { public: - static inline std::string name() + static inline void construct(duk::Context *ctx, ElapsedTimer *timer) { - return "\xff""\xff""ElapsedTimer"; + duk::StackAssert sa(ctx); + + duk::push(ctx, duk::This()); + duk::putProperty<void *>(ctx, -1, Signature, timer); + duk::pop(ctx); } - static inline std::vector<std::string> inherits() + static inline ElapsedTimer *require(duk::Context *ctx, Index index) { - return {}; + auto ptr = static_cast<ElapsedTimer *>(duk::getProperty<void *>(ctx, index, Signature)); + + if (!ptr) + duk::raise(ctx, DUK_ERR_TYPE_ERROR, "not an ElapsedTimer object"); + + return ptr; } }; @@ -48,9 +63,9 @@ * * Pause the timer, without resetting the current elapsed time stored. */ -duk::Ret pause(duk::ContextPtr ctx) +duk::Ret pause(duk::Context *ctx) { - duk::self<duk::Pointer<ElapsedTimer>>(ctx)->pause(); + duk::self<ElapsedTimer *>(ctx)->pause(); return 0; } @@ -61,9 +76,9 @@ * * Reset the elapsed time to 0, the status is not modified. */ -duk::Ret reset(duk::ContextPtr ctx) +duk::Ret reset(duk::Context *ctx) { - duk::self<duk::Pointer<ElapsedTimer>>(ctx)->reset(); + duk::self<ElapsedTimer *>(ctx)->reset(); return 0; } @@ -74,9 +89,9 @@ * * Restart the timer without resetting the current elapsed time. */ -duk::Ret restart(duk::ContextPtr ctx) +duk::Ret restart(duk::Context *ctx) { - duk::self<duk::Pointer<ElapsedTimer>>(ctx)->restart(); + duk::self<ElapsedTimer *>(ctx)->restart(); return 0; } @@ -90,9 +105,9 @@ * Returns: * The time elapsed. */ -duk::Ret elapsed(duk::ContextPtr ctx) +duk::Ret elapsed(duk::Context *ctx) { - duk::push(ctx, (int)duk::self<duk::Pointer<ElapsedTimer>>(ctx)->elapsed()); + duk::push(ctx, static_cast<int>(duk::self<ElapsedTimer *>(ctx)->elapsed())); return 1; } @@ -103,9 +118,24 @@ * * Construct a new ElapsedTimer object. */ -duk::Ret constructor(duk::ContextPtr ctx) +duk::Ret constructor(duk::Context *ctx) { - duk::construct(ctx, duk::Pointer<ElapsedTimer>{new ElapsedTimer}); + duk::construct(ctx, new ElapsedTimer); + + return 0; +} + +/* + * Function: Irccd.ElapsedTimer() [destructor] + * ------------------------------------------------------------------ + * + * Delete the property. + */ +duk::Ret destructor(duk::Context *ctx) +{ + delete static_cast<ElapsedTimer *>(duk::getProperty<void *>(ctx, 0, Signature)); + + duk::deleteProperty(ctx, 0, Signature); return 0; } @@ -131,7 +161,9 @@ duk::getGlobal<void>(plugin.context(), "Irccd"); duk::push(plugin.context(), duk::Function{constructor, 0}); duk::push(plugin.context(), duk::Object{}); - duk::push(plugin.context(), methods); + duk::put(plugin.context(), methods); + duk::push(plugin.context(), duk::Function(destructor, 1)); + duk::setFinalizer(plugin.context(), -2); duk::putProperty(plugin.context(), -2, "prototype"); duk::putProperty(plugin.context(), -2, "ElapsedTimer"); duk::pop(plugin.context());
--- a/lib/irccd/mod-elapsed-timer.hpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/mod-elapsed-timer.hpp Tue May 31 21:03:01 2016 +0200 @@ -41,7 +41,7 @@ /** * \copydoc Module::load */ - IRCCD_EXPORT virtual void load(Irccd &irccd, JsPlugin &plugin); + IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override; }; } // !irccd
--- a/lib/irccd/mod-file.cpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/mod-file.cpp Tue May 31 21:03:01 2016 +0200 @@ -18,6 +18,7 @@ #include <algorithm> #include <array> +#include <cassert> #include <iterator> #include <vector> @@ -35,6 +36,13 @@ namespace irccd { +namespace { + +const std::string Signature("\xff""\xff""irccd-file-ptr"); +const std::string Prototype("\xff""\xff""irccd-file-prototype"); + +} // !namespace + #if defined(HAVE_STAT) /* @@ -47,7 +55,7 @@ template <> class TypeTraits<struct stat> { public: - static void push(ContextPtr ctx, const struct stat &st) + static void push(Context *ctx, const struct stat &st) { duk::push(ctx, Object{}); @@ -104,7 +112,7 @@ * ------------------------------------------------------------------ */ -/* Remove trailing \r for CRLF line style */ +// Remove trailing \r for CRLF line style. inline std::string clearCr(std::string input) { if (input.length() > 0 && input.back() == '\r') @@ -127,9 +135,9 @@ * Returns: * The base name. */ -duk::Ret methodBasename(duk::ContextPtr ctx) +duk::Ret methodBasename(duk::Context *ctx) { - duk::push(ctx, fs::baseName(duk::self<duk::Pointer<File>>(ctx)->path())); + duk::push(ctx, fs::baseName(duk::self<File *>(ctx)->path())); return 1; } @@ -140,9 +148,9 @@ * * Force close of the file, automatically called when object is collected. */ -duk::Ret methodClose(duk::ContextPtr ctx) +duk::Ret methodClose(duk::Context *ctx) { - duk::self<duk::Pointer<File>>(ctx)->close(); + duk::self<File *>(ctx)->close(); return 0; } @@ -156,9 +164,9 @@ * Returns: * The directory name. */ -duk::Ret methodDirname(duk::ContextPtr ctx) +duk::Ret methodDirname(duk::Context *ctx) { - duk::push(ctx, fs::dirName(duk::self<duk::Pointer<File>>(ctx)->path())); + duk::push(ctx, fs::dirName(duk::self<File *>(ctx)->path())); return 1; } @@ -174,11 +182,11 @@ * Throws * - Any exception on error. */ -duk::Ret methodLines(duk::ContextPtr ctx) +duk::Ret methodLines(duk::Context *ctx) { duk::push(ctx, duk::Array{}); - std::FILE *fp = duk::self<duk::Pointer<File>>(ctx)->handle(); + std::FILE *fp = duk::self<File *>(ctx)->handle(); std::string buffer; std::array<char, 128> data; std::int32_t i = 0; @@ -218,10 +226,10 @@ * Throws: * - Any exception on error. */ -duk::Ret methodRead(duk::ContextPtr ctx) +duk::Ret methodRead(duk::Context *ctx) { auto amount = duk::optional<int>(ctx, 0, -1); - auto file = duk::self<duk::Pointer<File>>(ctx); + auto file = duk::self<File *>(ctx); if (amount == 0 || file->handle() == nullptr) return 0; @@ -272,9 +280,9 @@ * Throws: * - Any exception on error. */ -duk::Ret methodReadline(duk::ContextPtr ctx) +duk::Ret methodReadline(duk::Context *ctx) { - std::FILE *fp = duk::self<duk::Pointer<File>>(ctx)->handle(); + std::FILE *fp = duk::self<File *>(ctx)->handle(); std::string result; if (fp == nullptr || std::feof(fp)) @@ -298,9 +306,9 @@ * Throws: * - Any exception on error. */ -duk::Ret methodRemove(duk::ContextPtr ctx) +duk::Ret methodRemove(duk::Context *ctx) { - if (::remove(duk::self<duk::Pointer<File>>(ctx)->path().c_str()) < 0) + if (::remove(duk::self<File *>(ctx)->path().c_str()) < 0) duk::raise(ctx, SystemError()); return 0; @@ -318,11 +326,11 @@ * Throws: * - Any exception on error. */ -duk::Ret methodSeek(duk::ContextPtr ctx) +duk::Ret methodSeek(duk::Context *ctx) { auto type = duk::require<int>(ctx, 0); auto amount = duk::require<int>(ctx, 1); - auto fp = duk::self<duk::Pointer<File>>(ctx)->handle(); + auto fp = duk::self<File *>(ctx)->handle(); if (fp != nullptr && std::fseek(fp, amount, type) != 0) duk::raise(ctx, SystemError()); @@ -343,10 +351,10 @@ * Throws: * - Any exception on error. */ -duk::Ret methodStat(duk::ContextPtr ctx) +duk::Ret methodStat(duk::Context *ctx) { struct stat st; - auto file = duk::self<duk::Pointer<File>>(ctx); + auto file = duk::self<File *>(ctx); if (file->handle() == nullptr && ::stat(file->path().c_str(), &st) < 0) duk::raise(ctx, SystemError()); @@ -369,9 +377,9 @@ * Throws: * - Any exception on error. */ -duk::Ret methodTell(duk::ContextPtr ctx) +duk::Ret methodTell(duk::Context *ctx) { - auto fp = duk::self<duk::Pointer<File>>(ctx)->handle(); + auto fp = duk::self<File *>(ctx)->handle(); long pos; if (fp == nullptr) @@ -398,9 +406,9 @@ * Throws: * - Any exception on error. */ -duk::Ret methodWrite(duk::ContextPtr ctx) +duk::Ret methodWrite(duk::Context *ctx) { - std::FILE *fp = duk::self<duk::Pointer<File>>(ctx)->handle(); + std::FILE *fp = duk::self<File *>(ctx)->handle(); std::string data = duk::require<std::string>(ctx, 0); if (fp == nullptr) @@ -449,16 +457,13 @@ * Throws: * - Any exception on error. */ -duk::Ret constructor(duk::ContextPtr ctx) +duk::Ret constructor(duk::Context *ctx) { - if (!duk_is_constructor_call(ctx)) + if (!duk::isConstructorCall(ctx)) return 0; - std::string path = duk::require<std::string>(ctx, 0); - std::string mode = duk::require<std::string>(ctx, 1); - try { - duk::construct(ctx, duk::Pointer<File>{new File(path, mode)}); + duk::construct(ctx, new File(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1))); } catch (const std::exception &) { duk::raise(ctx, SystemError()); } @@ -467,6 +472,21 @@ } /* + * Function: Irccd.File() [destructor] + * ------------------------------------------------------------------ + * + * Delete the property. + */ +duk::Ret destructor(duk::Context *ctx) +{ + delete static_cast<File *>(duk::getProperty<void *>(ctx, 0, Signature)); + + duk::deleteProperty(ctx, 0, Signature); + + return 0; +} + +/* * Function: Irccd.File.basename(path) * -------------------------------------------------------- * @@ -477,7 +497,7 @@ * Returns: * The base name. */ -duk::Ret functionBasename(duk::ContextPtr ctx) +duk::Ret functionBasename(duk::Context *ctx) { duk::push(ctx, fs::baseName(duk::require<std::string>(ctx, 0))); @@ -495,7 +515,7 @@ * Returns: * The directory name. */ -duk::Ret functionDirname(duk::ContextPtr ctx) +duk::Ret functionDirname(duk::Context *ctx) { duk::push(ctx, fs::dirName( duk::require<std::string>(ctx, 0))); @@ -515,7 +535,7 @@ * Throws: * - Any exception if we don't have access. */ -duk::Ret functionExists(duk::ContextPtr ctx) +duk::Ret functionExists(duk::Context *ctx) { duk::push(ctx, fs::exists(duk::require<std::string>(ctx, 0))); @@ -533,7 +553,7 @@ * Throws: * - Any exception on error. */ -duk::Ret functionRemove(duk::ContextPtr ctx) +duk::Ret functionRemove(duk::Context *ctx) { if (::remove(duk::require<std::string>(ctx, 0).c_str()) < 0) duk::raise(ctx, SystemError()); @@ -556,7 +576,7 @@ * Throws: * - Any exception on error. */ -duk::Ret functionStat(duk::ContextPtr ctx) +duk::Ret functionStat(duk::Context *ctx) { struct stat st; @@ -588,6 +608,41 @@ } // !namespace +namespace duk { + +void TypeTraits<File *>::construct(duk::Context *ctx, File *fp) +{ + assert(fp); + + duk::StackAssert sa(ctx); + + duk::push(ctx, duk::This()); + duk::putProperty<void *>(ctx, -1, Signature, fp); + duk::pop(ctx); +} + +void TypeTraits<File *>::push(duk::Context *ctx, File *fp) +{ + duk::StackAssert sa(ctx, 1); + + duk::push(ctx, duk::Object()); + duk::putProperty<void *>(ctx, -1, Signature, fp); + duk::getGlobal<void>(ctx, Prototype); + duk::setPrototype(ctx, -2); +} + +File *TypeTraits<File *>::require(duk::Context *ctx, Index index) +{ + auto ptr = static_cast<File *>(duk::getProperty<void *>(ctx, index, Signature)); + + if (!ptr) + duk::raise(ctx, DUK_ERR_TYPE_ERROR, "not a File object"); + + return ptr; +} + +} // !duk + FileModule::FileModule() noexcept : Module("Irccd.File") { @@ -598,11 +653,15 @@ duk::StackAssert sa(plugin.context()); duk::getGlobal<void>(plugin.context(), "Irccd"); - duk::push(plugin.context(), duk::Function{constructor, 2}); - duk::push(plugin.context(), constants); - duk::push(plugin.context(), functions); + duk::push(plugin.context(), duk::Function(constructor, 2)); + duk::put(plugin.context(), constants); + duk::put(plugin.context(), functions); duk::push(plugin.context(), duk::Object{}); - duk::push(plugin.context(), methods); + duk::put(plugin.context(), methods); + duk::push(plugin.context(), duk::Function(destructor, 1)); + duk::setFinalizer(plugin.context(), -2); + duk::dup(plugin.context(), -1); + duk::putGlobal(plugin.context(), Prototype); duk::putProperty(plugin.context(), -2, "prototype"); duk::putProperty(plugin.context(), -2, "File"); duk::pop(plugin.context());
--- a/lib/irccd/mod-file.hpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/mod-file.hpp Tue May 31 21:03:01 2016 +0200 @@ -142,41 +142,33 @@ * \brief JavaScript binding for File. */ template <> -class TypeTraits<File> { +class TypeTraits<File *> { public: /** - * Push the File prototype. + * Construct the file. * - * \param ctx the context + * \pre fp != nullptr + * \param ctx the the context + * \param fp the file */ - static inline void prototype(ContextPtr ctx) - { - getGlobal<void>(ctx, "Irccd"); - getGlobal<void>(ctx, "File"); - getProperty<void>(ctx, -1, "prototype"); - remove(ctx, -2); - remove(ctx, -2); - } + IRCCD_EXPORT static void construct(duk::Context *ctx, File *fp); /** - * Get the File signature. + * Push a file. * - * \return File + * \pre fp != nullptr + * \param ctx the the context + * \param fp the file */ - static inline std::string name() - { - return "\xff""\xff""File"; - } + IRCCD_EXPORT static void push(duk::Context *ctx, File *fp); /** - * Get the inheritance list. + * Require a file. Raises a JavaScript error if not a File. * - * \return empty + * \param ctx the context + * \param index the index */ - static inline std::vector<std::string> inherits() - { - return {}; - } + IRCCD_EXPORT static File *require(duk::Context *ctx, Index index); }; } // !duk
--- a/lib/irccd/mod-irccd.cpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/mod-irccd.cpp Tue May 31 21:03:01 2016 +0200 @@ -34,7 +34,7 @@ { } -void SystemError::raise(duk::ContextPtr ctx) const +void SystemError::raise(duk::Context *ctx) const { duk::StackAssert sa(ctx, 0); @@ -47,7 +47,7 @@ duk::raise(ctx); } -duk::Ret constructor(duk::ContextPtr ctx) +duk::Ret constructor(duk::Context *ctx) { duk::push(ctx, duk::This{}); duk::putProperty(ctx, -1, "errno", duk::require<int>(ctx, 0)); @@ -91,7 +91,7 @@ duk::putGlobal(plugin.context(), "Irccd"); // Store global instance. - duk::putGlobal(plugin.context(), "\xff""\xff""irccd", duk::RawPointer<Irccd>{&irccd}); + duk::putGlobal(plugin.context(), "\xff""\xff""irccd",&irccd); } } // !irccd
--- a/lib/irccd/mod-irccd.hpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/mod-irccd.hpp Tue May 31 21:03:01 2016 +0200 @@ -62,7 +62,7 @@ * * \param ctx the context */ - IRCCD_EXPORT void raise(duk::ContextPtr ctx) const; + IRCCD_EXPORT void raise(duk::Context *ctx) const; }; /**
--- a/lib/irccd/mod-logger.cpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/mod-logger.cpp Tue May 31 21:03:01 2016 +0200 @@ -24,7 +24,7 @@ namespace { -duk::Ret print(duk::ContextPtr ctx, std::ostream &out) +duk::Ret print(duk::Context *ctx, std::ostream &out) { /* * Get the message before we start printing stuff to avoid @@ -45,7 +45,7 @@ * Arguments: * - message, the message. */ -duk::Ret info(duk::ContextPtr ctx) +duk::Ret info(duk::Context *ctx) { return print(ctx, log::info()); } @@ -59,7 +59,7 @@ * Arguments: * - message, the warning. */ -duk::Ret warning(duk::ContextPtr ctx) +duk::Ret warning(duk::Context *ctx) { return print(ctx, log::warning()); } @@ -73,7 +73,7 @@ * Arguments: * - message, the message. */ -duk::Ret debug(duk::ContextPtr ctx) +duk::Ret debug(duk::Context *ctx) { return print(ctx, log::debug()); } @@ -97,7 +97,7 @@ duk::getGlobal<void>(plugin.context(), "Irccd"); duk::push(plugin.context(), duk::Object{}); - duk::push(plugin.context(), functions); + duk::put(plugin.context(), functions); duk::putProperty(plugin.context(), -2, "Logger"); duk::pop(plugin.context()); }
--- a/lib/irccd/mod-plugin.cpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/mod-plugin.cpp Tue May 31 21:03:01 2016 +0200 @@ -25,6 +25,8 @@ namespace { +const std::string PluginGlobal{"\xff""\xff""irccd-plugin-ptr"}; + /* * Wrap function for these functions because they all takes the same arguments. * @@ -33,12 +35,12 @@ * - unload. */ template <typename Func> -duk::Ret wrap(duk::ContextPtr ctx, int nret, Func &&func) +duk::Ret wrap(duk::Context *ctx, int nret, Func &&func) { std::string name = duk::require<std::string>(ctx, 0); try { - func(*duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd"), name); + func(*duk::getGlobal<Irccd *>(ctx, "\xff""\xff""irccd"), name); } catch (const std::out_of_range &ex) { duk::raise(ctx, duk::ReferenceError(ex.what())); } catch (const std::exception &ex) { @@ -67,14 +69,14 @@ * Returns: * The plugin information or undefined if the plugin was not found. */ -duk::Ret info(duk::ContextPtr ctx) +duk::Ret info(duk::Context *ctx) { Plugin *plugin = nullptr; if (duk::top(ctx) >= 1) - plugin = duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->pluginService().get(duk::require<std::string>(ctx, 0)).get(); + plugin = duk::getGlobal<Irccd *>(ctx, "\xff""\xff""irccd")->pluginService().get(duk::require<std::string>(ctx, 0)).get(); else - plugin = duk::getGlobal<duk::RawPointer<Plugin>>(ctx, "\xff""\xff""plugin"); + plugin = duk::getGlobal<Plugin *>(ctx, "\xff""\xff""plugin"); if (!plugin) return 0; @@ -98,12 +100,12 @@ * Returns: * The list of all plugin names. */ -duk::Ret list(duk::ContextPtr ctx) +duk::Ret list(duk::Context *ctx) { duk::push(ctx, duk::Array{}); int i = 0; - for (const auto &plugin : duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->pluginService().plugins()) + for (const auto &plugin : duk::getGlobal<Irccd *>(ctx, "\xff""\xff""irccd")->pluginService().plugins()) duk::putProperty(ctx, -1, i++, plugin->name()); return 1; @@ -121,7 +123,7 @@ * - Error on errors, * - ReferenceError if the plugin was not found. */ -duk::Ret load(duk::ContextPtr ctx) +duk::Ret load(duk::Context *ctx) { return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) { irccd.pluginService().load(name); @@ -140,7 +142,7 @@ * - Error on errors, * - ReferenceError if the plugin was not found. */ -duk::Ret reload(duk::ContextPtr ctx) +duk::Ret reload(duk::Context *ctx) { return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) { irccd.pluginService().reload(name); @@ -159,7 +161,7 @@ * - Error on errors, * - ReferenceError if the plugin was not found. */ -duk::Ret unload(duk::ContextPtr ctx) +duk::Ret unload(duk::Context *ctx) { return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) { irccd.pluginService().unload(name); @@ -176,6 +178,15 @@ } // !namespace +namespace duk { + +std::shared_ptr<JsPlugin> TypeTraits<std::shared_ptr<JsPlugin>>::get(duk::Context *ctx) +{ + return std::static_pointer_cast<JsPlugin>(static_cast<Plugin *>(duk::getGlobal<void *>(ctx, PluginGlobal))->shared_from_this()); +} + +} // !duk + PluginModule::PluginModule() noexcept : Module("Irccd.Plugin") { @@ -185,9 +196,10 @@ { duk::StackAssert sa(plugin.context()); + duk::putGlobal<void *>(plugin.context(), PluginGlobal, &plugin); duk::getGlobal<void>(plugin.context(), "Irccd"); duk::push(plugin.context(), duk::Object{}); - duk::push(plugin.context(), functions); + duk::put(plugin.context(), functions); duk::push(plugin.context(), duk::Object{}); duk::putProperty(plugin.context(), -2, "config"); duk::push(plugin.context(), duk::Object{});
--- a/lib/irccd/mod-plugin.hpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/mod-plugin.hpp Tue May 31 21:03:01 2016 +0200 @@ -24,10 +24,31 @@ * \brief Irccd.Plugin JavaScript API. */ +#include "js.hpp" #include "module.hpp" namespace irccd { +namespace duk { + +/** + * \brief Custom specialization for JsPlugin. + * \note Use the dedicated get function to get the underlying JsPlugin. + */ +template <> +class TypeTraits<std::shared_ptr<JsPlugin>> { +public: + /** + * Get the plugin. + * + * \param ctx the context + * \return the plugin + */ + IRCCD_EXPORT static std::shared_ptr<JsPlugin> get(duk::Context *ctx); +}; + +} // !duk + /** * \brief Irccd.Plugin JavaScript API. */ @@ -41,7 +62,7 @@ /** * \copydoc Module::load */ - IRCCD_EXPORT virtual void load(Irccd &irccd, JsPlugin &plugin); + IRCCD_EXPORT void load(Irccd &irccd, JsPlugin &plugin) override; }; } // !irccd
--- a/lib/irccd/mod-server.cpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/mod-server.cpp Tue May 31 21:03:01 2016 +0200 @@ -16,6 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <cassert> #include <sstream> #include <unordered_map> @@ -29,6 +30,9 @@ namespace { +const std::string Signature("\xff""\xff""irccd-server-ptr"); +const std::string Prototype("\xff""\xff""irccd-server-prototype"); + /* * Method: Server.cmode(channel, mode) * ------------------------------------------------------------------ @@ -39,9 +43,9 @@ * - channel, the channel, * - mode, the mode. */ -duk::Ret cmode(duk::ContextPtr ctx) +duk::Ret cmode(duk::Context *ctx) { - duk::self<duk::Shared<Server>>(ctx)->cmode(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); + duk::self<std::shared_ptr<Server>>(ctx)->cmode(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); return 0; } @@ -56,9 +60,9 @@ * - channel, the channel, * - message, the message. */ -duk::Ret cnotice(duk::ContextPtr ctx) +duk::Ret cnotice(duk::Context *ctx) { - duk::self<duk::Shared<Server>>(ctx)->cnotice(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); + duk::self<std::shared_ptr<Server>>(ctx)->cnotice(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); return 0; } @@ -76,9 +80,9 @@ * sslVerify: true if ssl was verified * channels: an array of all channels */ -duk::Ret info(duk::ContextPtr ctx) +duk::Ret info(duk::Context *ctx) { - auto server = duk::self<duk::Shared<Server>>(ctx); + auto server = duk::self<std::shared_ptr<Server>>(ctx); duk::push(ctx, duk::Object{}); duk::putProperty(ctx, -1, "name", server->name()); @@ -113,9 +117,9 @@ * - target, the target to invite, * - channel, the channel. */ -duk::Ret invite(duk::ContextPtr ctx) +duk::Ret invite(duk::Context *ctx) { - duk::self<duk::Shared<Server>>(ctx)->invite(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); + duk::self<std::shared_ptr<Server>>(ctx)->invite(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); return 0; } @@ -130,9 +134,9 @@ * - channel, the channel to join, * - password, the password or undefined to not use. */ -duk::Ret join(duk::ContextPtr ctx) +duk::Ret join(duk::Context *ctx) { - duk::self<duk::Shared<Server>>(ctx)->join(duk::require<std::string>(ctx, 0), duk::optional<std::string>(ctx, 1, "")); + duk::self<std::shared_ptr<Server>>(ctx)->join(duk::require<std::string>(ctx, 0), duk::optional<std::string>(ctx, 1, "")); return 0; } @@ -148,9 +152,9 @@ * - channel, the channel, * - reason, the optional reason or undefined to not set. */ -duk::Ret kick(duk::ContextPtr ctx) +duk::Ret kick(duk::Context *ctx) { - duk::self<duk::Shared<Server>>(ctx)->kick( + duk::self<std::shared_ptr<Server>>(ctx)->kick( duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1), duk::optional<std::string>(ctx, 2, "") @@ -169,9 +173,9 @@ * - target, the target or a channel, * - message, the message. */ -duk::Ret me(duk::ContextPtr ctx) +duk::Ret me(duk::Context *ctx) { - duk::self<duk::Shared<Server>>(ctx)->me(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); + duk::self<std::shared_ptr<Server>>(ctx)->me(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); return 0; } @@ -186,9 +190,9 @@ * - target, the target or a channel, * - message, the message. */ -duk::Ret message(duk::ContextPtr ctx) +duk::Ret message(duk::Context *ctx) { - duk::self<duk::Shared<Server>>(ctx)->message(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); + duk::self<std::shared_ptr<Server>>(ctx)->message(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); return 0; } @@ -202,9 +206,9 @@ * Arguments: * - mode, the new mode. */ -duk::Ret mode(duk::ContextPtr ctx) +duk::Ret mode(duk::Context *ctx) { - duk::self<duk::Shared<Server>>(ctx)->mode(duk::require<std::string>(ctx, 0)); + duk::self<std::shared_ptr<Server>>(ctx)->mode(duk::require<std::string>(ctx, 0)); return 0; } @@ -218,9 +222,9 @@ * Arguments: * - channel, the channel. */ -duk::Ret names(duk::ContextPtr ctx) +duk::Ret names(duk::Context *ctx) { - duk::self<duk::Shared<Server>>(ctx)->names(duk::require<std::string>(ctx, 0)); + duk::self<std::shared_ptr<Server>>(ctx)->names(duk::require<std::string>(ctx, 0)); return 0; } @@ -234,9 +238,9 @@ * Arguments: * - nickname, the nickname. */ -duk::Ret nick(duk::ContextPtr ctx) +duk::Ret nick(duk::Context *ctx) { - duk::self<duk::Shared<Server>>(ctx)->nick(duk::require<std::string>(ctx, 0)); + duk::self<std::shared_ptr<Server>>(ctx)->nick(duk::require<std::string>(ctx, 0)); return 0; } @@ -251,9 +255,9 @@ * - target, the target, * - message, the notice message. */ -duk::Ret notice(duk::ContextPtr ctx) +duk::Ret notice(duk::Context *ctx) { - duk::self<duk::Shared<Server>>(ctx)->notice(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); + duk::self<std::shared_ptr<Server>>(ctx)->notice(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); return 0; } @@ -268,9 +272,9 @@ * - channel, the channel to leave, * - reason, the optional reason, keep undefined for portability. */ -duk::Ret part(duk::ContextPtr ctx) +duk::Ret part(duk::Context *ctx) { - duk::self<duk::Shared<Server>>(ctx)->part(duk::require<std::string>(ctx, 0), duk::optional<std::string>(ctx, 1, "")); + duk::self<std::shared_ptr<Server>>(ctx)->part(duk::require<std::string>(ctx, 0), duk::optional<std::string>(ctx, 1, "")); return 0; } @@ -284,9 +288,9 @@ * Arguments: * - raw, the raw message (without terminators). */ -duk::Ret send(duk::ContextPtr ctx) +duk::Ret send(duk::Context *ctx) { - duk::self<duk::Shared<Server>>(ctx)->send(duk::require<std::string>(ctx, 0)); + duk::self<std::shared_ptr<Server>>(ctx)->send(duk::require<std::string>(ctx, 0)); return 0; } @@ -301,9 +305,9 @@ * - channel, the channel, * - topic, the new topic. */ -duk::Ret topic(duk::ContextPtr ctx) +duk::Ret topic(duk::Context *ctx) { - duk::self<duk::Shared<Server>>(ctx)->topic(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); + duk::self<std::shared_ptr<Server>>(ctx)->topic(duk::require<std::string>(ctx, 0), duk::require<std::string>(ctx, 1)); return 0; } @@ -317,9 +321,9 @@ * Arguments: * - target, the target. */ -duk::Ret whois(duk::ContextPtr ctx) +duk::Ret whois(duk::Context *ctx) { - duk::self<duk::Shared<Server>>(ctx)->whois(duk::require<std::string>(ctx, 0)); + duk::self<std::shared_ptr<Server>>(ctx)->whois(duk::require<std::string>(ctx, 0)); return 0; } @@ -334,9 +338,9 @@ * Returns: * The server name (unique). */ -duk::Ret toString(duk::ContextPtr ctx) +duk::Ret toString(duk::Context *ctx) { - duk::push(ctx, duk::self<duk::Shared<Server>>(ctx)->name()); + duk::push(ctx, duk::self<std::shared_ptr<Server>>(ctx)->name()); return 1; } @@ -362,9 +366,9 @@ * realname: "real name", (Optional, default: IRC Client Daemon) * commandChar: "!", (Optional, the command char, default: "!") */ -duk::Ret constructor(duk::ContextPtr ctx) +duk::Ret constructor(duk::Context *ctx) { - if (!duk_is_constructor_call(ctx)) + if (!duk::isConstructorCall(ctx)) return 0; std::string name; @@ -375,12 +379,11 @@ // Information part. name = duk::getProperty<std::string>(ctx, 0, "name"); info.host = duk::getProperty<std::string>(ctx, 0, "host"); - info.port = duk::optionalProperty<int>(ctx, 0, "port", (int)info.port); + info.port = duk::optionalProperty<int>(ctx, 0, "port", static_cast<int>(info.port)); info.password = duk::optionalProperty<std::string>(ctx, 0, "password", ""); - if (duk::optionalProperty<bool>(ctx, 0, "ipv6", false)) { + if (duk::optionalProperty<bool>(ctx, 0, "ipv6", false)) info.flags |= ServerInfo::Ipv6; - } // Identity part. identity.nickname = duk::optionalProperty<std::string>(ctx, 0, "nickname", identity.nickname); @@ -401,8 +404,7 @@ settings.flags |= ServerSettings::AutoRejoin; try { - duk::construct(ctx, duk::Shared<Server>{std::make_shared<Server>(std::move(name), std::move(info), - std::move(identity), std::move(settings))}); + duk::construct(ctx, std::make_shared<Server>(std::move(name), std::move(info), std::move(identity), std::move(settings))); } catch (const std::exception &ex) { duk::raise(ctx, duk::Error(ex.what())); } @@ -411,6 +413,22 @@ } /* + * Function: Irccd.Server() [destructor] + * ------------------------------------------------------------------ + * + * Delete the property. + */ +duk::Ret destructor(duk::Context *ctx) +{ + delete static_cast<std::shared_ptr<Server> *>(duk::getProperty<void *>(ctx, 0, Signature)); + + duk::deleteProperty(ctx, 0, Signature); + + return 0; +} + + +/* * Function: Irccd.Server.add(s) * ------------------------------------------------------------------ * @@ -419,12 +437,9 @@ * Arguments: * - s, the server to add. */ -duk::Ret add(duk::ContextPtr ctx) +duk::Ret add(duk::Context *ctx) { - auto server = duk::get<duk::Shared<Server>>(ctx, 0); - - if (server) - duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->serverService().add(server); + duk::getGlobal<Irccd *>(ctx, "\xff""\xff""irccd")->serverService().add(duk::require<std::shared_ptr<Server>>(ctx, 0)); return 0; } @@ -440,13 +455,13 @@ * Returns: * The server object or undefined if not found. */ -duk::Ret find(duk::ContextPtr ctx) +duk::Ret find(duk::Context *ctx) { const auto name = duk::require<std::string>(ctx, 0); - const auto irccd = duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd"); + const auto irccd = duk::getGlobal<Irccd *>(ctx, "\xff""\xff""irccd"); try { - duk::push(ctx, duk::Shared<Server>{irccd->serverService().require(name)}); + duk::push(ctx, irccd->serverService().require(name)); } catch (...) { return 0; } @@ -463,12 +478,12 @@ * Returns: * An object with string-to-servers pairs. */ -duk::Ret list(duk::ContextPtr ctx) +duk::Ret list(duk::Context *ctx) { duk::push(ctx, duk::Object{}); - for (const auto &server : duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->serverService().servers()) - duk::putProperty(ctx, -1, server->name(), duk::Shared<Server>{server}); + for (const auto &server : duk::getGlobal<Irccd *>(ctx, "\xff""\xff""irccd")->serverService().servers()) + duk::putProperty(ctx, -1, server->name(), server); return 1; } @@ -482,9 +497,9 @@ * Arguments: * - name the server name. */ -duk::Ret remove(duk::ContextPtr ctx) +duk::Ret remove(duk::Context *ctx) { - duk::getGlobal<duk::RawPointer<Irccd>>(ctx, "\xff""\xff""irccd")->serverService().remove(duk::require<std::string>(ctx, 0)); + duk::getGlobal<Irccd *>(ctx, "\xff""\xff""irccd")->serverService().remove(duk::require<std::string>(ctx, 0)); return 0; } @@ -518,6 +533,43 @@ } // !namespace +namespace duk { + +void TypeTraits<std::shared_ptr<Server>>::construct(Context *ctx, std::shared_ptr<Server> server) +{ + assert(server); + + duk::StackAssert sa(ctx); + + duk::push(ctx, This()); + duk::putProperty(ctx, -1, Signature, new std::shared_ptr<Server>(std::move(server))); + duk::pop(ctx); +} + +void TypeTraits<std::shared_ptr<Server>>::push(Context *ctx, std::shared_ptr<Server> server) +{ + assert(server); + + duk::StackAssert sa(ctx, 1); + + duk::push(ctx, Object()); + duk::putProperty(ctx, -1, Signature, new std::shared_ptr<Server>(std::move(server))); + duk::getGlobal<void>(ctx, Prototype); + duk::setPrototype(ctx, -2); +} + +std::shared_ptr<Server> TypeTraits<std::shared_ptr<Server>>::require(Context *ctx, Index index) +{ + auto ptr = getProperty<std::shared_ptr<Server> *>(ctx, index, Signature); + + if (!ptr) + duk::raise(ctx, DUK_ERR_TYPE_ERROR, "not a Server object"); + + return *ptr; +} + +} // !duk + ServerModule::ServerModule() noexcept : Module("Irccd.Server") { @@ -528,10 +580,14 @@ duk::StackAssert sa(plugin.context()); duk::getGlobal<void>(plugin.context(), "Irccd"); - duk::push(plugin.context(), duk::Function{constructor, 1}); - duk::push(plugin.context(), functions); + duk::push(plugin.context(), duk::Function(constructor, 1)); + duk::put(plugin.context(), functions); duk::push(plugin.context(), duk::Object()); - duk::push(plugin.context(), methods); + duk::put(plugin.context(), methods); + duk::push(plugin.context(), duk::Function(destructor, 1)); + duk::setFinalizer(plugin.context(), -2); + duk::dup(plugin.context()); + duk::putGlobal(plugin.context(), Prototype); duk::putProperty(plugin.context(), -2, "prototype"); duk::putProperty(plugin.context(), -2, "Server"); duk::pop(plugin.context());
--- a/lib/irccd/mod-server.hpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/mod-server.hpp Tue May 31 21:03:01 2016 +0200 @@ -36,41 +36,34 @@ * \brief JavaScript binding for Server. */ template <> -class TypeTraits<irccd::Server> { +class TypeTraits<std::shared_ptr<Server>> { public: /** - * Push the Server prototype. + * Construct a server. * + * \pre server != nullptr * \param ctx the context + * \param server the server */ - static inline void prototype(ContextPtr ctx) - { - getGlobal<void>(ctx, "Irccd"); - getProperty<void>(ctx, -1, "Server"); - getProperty<void>(ctx, -1, "prototype"); - remove(ctx, -2); - remove(ctx, -2); - } + IRCCD_EXPORT static void construct(Context *ctx, std::shared_ptr<Server> server); /** - * Get the Server signature. + * Push a server. * - * \return Server + * \pre server != nullptr + * \param ctx the context + * \param server the server */ - static inline std::string name() - { - return "\xff""\xff""Server"; - } + IRCCD_EXPORT static void push(Context *ctx, std::shared_ptr<Server>); /** - * Get the inheritance list. + * Require a server. Raise a JavaScript error if not a Server. * - * \return empty + * \param ctx the context + * \param index the index + * \return the server */ - static inline std::vector<std::string> inherits() - { - return {}; - } + IRCCD_EXPORT static std::shared_ptr<Server> require(Context *ctx, Index index); }; } // !duk
--- a/lib/irccd/mod-system.cpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/mod-system.cpp Tue May 31 21:03:01 2016 +0200 @@ -47,7 +47,7 @@ * Returns: * The value. */ -int env(duk::ContextPtr ctx) +int env(duk::Context *ctx) { duk::push(ctx, sys::env(duk::get<std::string>(ctx, 0))); @@ -63,7 +63,7 @@ * Arguments: * - cmd, the command to execute. */ -int exec(duk::ContextPtr ctx) +int exec(duk::Context *ctx) { std::system(duk::get<const char *>(ctx, 0)); @@ -79,7 +79,7 @@ * Returns: * The user home directory. */ -int home(duk::ContextPtr ctx) +int home(duk::Context *ctx) { duk::push(ctx, sys::home()); @@ -95,7 +95,7 @@ * Returns: * The system name. */ -int name(duk::ContextPtr ctx) +int name(duk::Context *ctx) { duk::push(ctx, sys::name()); @@ -118,14 +118,14 @@ * Throws * - Irccd.SystemError on failures. */ -int popen(duk::ContextPtr ctx) +int popen(duk::Context *ctx) { auto fp = ::popen(duk::require<const char *>(ctx, 0), duk::require<const char *>(ctx, 1)); if (fp == nullptr) duk::raise(ctx, SystemError{}); - duk::push(ctx, duk::Pointer<File>{new File(fp, [] (std::FILE *fp) { ::pclose(fp); })}); + duk::push(ctx, new File(fp, [] (std::FILE *fp) { ::pclose(fp); })); return 1; } @@ -138,7 +138,7 @@ * * Sleep the main loop for the specific delay in seconds. */ -int sleep(duk::ContextPtr ctx) +int sleep(duk::Context *ctx) { std::this_thread::sleep_for(std::chrono::seconds(duk::get<int>(ctx, 0))); @@ -154,7 +154,7 @@ * Returns: * The number of milliseconds. */ -int ticks(duk::ContextPtr ctx) +int ticks(duk::Context *ctx) { duk::push(ctx, static_cast<int>(sys::ticks())); @@ -167,7 +167,7 @@ * * Sleep the main loop for the specific delay in microseconds. */ -int usleep(duk::ContextPtr ctx) +int usleep(duk::Context *ctx) { std::this_thread::sleep_for(std::chrono::microseconds(duk::get<int>(ctx, 0))); @@ -183,7 +183,7 @@ * Returns: * The system uptime. */ -int uptime(duk::ContextPtr ctx) +int uptime(duk::Context *ctx) { duk::push<int>(ctx, sys::uptime()); @@ -199,7 +199,7 @@ * Returns: * The system version. */ -int version(duk::ContextPtr ctx) +int version(duk::Context *ctx) { duk::push(ctx, sys::version()); @@ -234,7 +234,7 @@ duk::getGlobal<void>(plugin.context(), "Irccd"); duk::push(plugin.context(), duk::Object{}); - duk::push(plugin.context(), functions); + duk::put(plugin.context(), functions); duk::putProperty(plugin.context(), -2, "System"); duk::pop(plugin.context()); }
--- a/lib/irccd/mod-timer.cpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/mod-timer.cpp Tue May 31 21:03:01 2016 +0200 @@ -21,6 +21,7 @@ #include "irccd.hpp" #include "logger.hpp" #include "mod-timer.hpp" +#include "mod-plugin.hpp" #include "plugin-js.hpp" #include "timer.hpp" @@ -28,19 +29,35 @@ namespace irccd { +namespace { + +const std::string Signature{"\xff""\xff""irccd-timer-ptr"}; +const std::string CallbackTable{"\xff""\xff""irccd-timer-callbacks"}; + +} // !namespace + namespace duk { template <> -class TypeTraits<Timer> { +class TypeTraits<std::shared_ptr<Timer>> { public: - static std::string name() noexcept + static inline void construct(duk::Context *ctx, std::shared_ptr<Timer> timer) { - return "\xff""\xff""Timer"; + duk::StackAssert sa(ctx); + + duk::push(ctx, duk::This()); + duk::putProperty<void *>(ctx, -1, Signature, new std::shared_ptr<Timer>(std::move(timer))); + duk::pop(ctx); } - static std::vector<std::string> inherits() + static inline std::shared_ptr<Timer> require(duk::Context *ctx, duk::Index index) { - return {}; + auto ptr = static_cast<std::shared_ptr<Timer> *>(duk::getProperty<void *>(ctx, index, Signature)); + + if (!ptr) + duk::raise(ctx, DUK_ERR_TYPE_ERROR, "not a Timer object"); + + return *ptr; } }; @@ -48,8 +65,6 @@ namespace { -const std::string CallbackTable{"\xff""\xff""irccd-timer-callbacks"}; - void handleSignal(std::weak_ptr<JsPlugin> ptr, std::string key) { auto plugin = ptr.lock(); @@ -57,7 +72,7 @@ if (!plugin) return; - auto irccd = duk::getGlobal<duk::RawPointer<Irccd>>(plugin->context(), "\xff""\xff""irccd"); + auto irccd = duk::getGlobal<Irccd *>(plugin->context(), "\xff""\xff""irccd"); irccd->post([plugin, key] (Irccd &) { duk::StackAssert sa(plugin->context()); @@ -68,9 +83,9 @@ if (duk::is<duk::Function>(plugin->context(), -1)) { if (duk::pcall(plugin->context()) != 0) - log::warning("plugin {}: {}"_format(plugin->name(), duk::error(plugin->context(), -1).stack)); - - duk::pop(plugin->context()); + log::warning("plugin {}: {}"_format(plugin->name(), duk::exception(plugin->context(), -1).stack)); + else + duk::pop(plugin->context()); } else duk::pop(plugin->context()); }); @@ -82,9 +97,9 @@ * * Start the timer. If the timer is already started the method is a no-op. */ -duk::Ret start(duk::ContextPtr ctx) +duk::Ret start(duk::Context *ctx) { - auto timer = duk::self<duk::Shared<Timer>>(ctx); + auto timer = duk::self<std::shared_ptr<Timer>>(ctx); if (!timer->isRunning()) timer->start(); @@ -98,9 +113,9 @@ * * Stop the timer. */ -duk::Ret stop(duk::ContextPtr ctx) +duk::Ret stop(duk::Context *ctx) { - auto timer = duk::self<duk::Shared<Timer>>(ctx); + auto timer = duk::self<std::shared_ptr<Timer>>(ctx); if (timer->isRunning()) timer->stop(); @@ -124,7 +139,7 @@ * - delay, the interval in milliseconds, * - callback, the function to call. */ -duk::Ret constructor(duk::ContextPtr ctx) +duk::Ret constructor(duk::Context *ctx) { // Check parameters. auto type = duk::require<int>(ctx, 0); @@ -139,19 +154,20 @@ // Construct the timer in 'this'. auto timer = std::make_shared<Timer>(static_cast<TimerType>(type), delay); - auto plugin = std::static_pointer_cast<JsPlugin>(duk::getGlobal<duk::RawPointer<Plugin>>(ctx, "\xff""\xff""plugin")->shared_from_this()); + auto plugin = duk::TypeTraits<std::shared_ptr<JsPlugin>>::get(ctx); auto hash = std::to_string(reinterpret_cast<std::uintptr_t>(timer.get())); timer->onSignal.connect(std::bind(handleSignal, std::weak_ptr<JsPlugin>(plugin), hash)); // Set a finalizer that closes the timer. - duk::construct(ctx, duk::Shared<Timer>{timer}); + duk::construct(ctx, std::shared_ptr<Timer>(std::move(timer))); duk::push(ctx, duk::This()); duk::putProperty(ctx, -1, "\xff""\xff""timer-key", hash); - duk::push(ctx, duk::Function{[] (duk::ContextPtr ctx) -> duk::Ret { + duk::push(ctx, duk::Function{[] (duk::Context *ctx) -> duk::Ret { duk::StackAssert sa(ctx); - duk::get<duk::Shared<Timer>>(ctx, 0)->stop(); + duk::require<std::shared_ptr<Timer>>(ctx, 0)->stop(); + delete static_cast<std::shared_ptr<Timer> *>(duk::getProperty<void *>(ctx, 0, Signature)); duk::getGlobal<void>(ctx, CallbackTable); duk::deleteProperty(ctx, -1, duk::getProperty<std::string>(ctx, 0, "\xff""\xff""timer-key")); duk::pop(ctx); @@ -188,9 +204,9 @@ duk::getGlobal<void>(plugin.context(), "Irccd"); duk::push(plugin.context(), duk::Function{constructor, 3}); - duk::push(plugin.context(), constants); + duk::put(plugin.context(), constants); duk::push(plugin.context(), duk::Object{}); - duk::push(plugin.context(), methods); + duk::put(plugin.context(), methods); duk::putProperty(plugin.context(), -2, "prototype"); duk::putProperty(plugin.context(), -2, "Timer"); duk::pop(plugin.context());
--- a/lib/irccd/mod-unicode.cpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/mod-unicode.cpp Tue May 31 21:03:01 2016 +0200 @@ -34,7 +34,7 @@ * Returns: * True if the code is in the digit category. */ -duk::Ret isDigit(duk::ContextPtr ctx) +duk::Ret isDigit(duk::Context *ctx) { duk::push(ctx, unicode::isdigit(duk::get<int>(ctx, 0))); @@ -50,7 +50,7 @@ * Returns: * True if the code is in the letter category. */ -duk::Ret isLetter(duk::ContextPtr ctx) +duk::Ret isLetter(duk::Context *ctx) { duk::push(ctx, unicode::isalpha(duk::get<int>(ctx, 0))); @@ -66,7 +66,7 @@ * Returns: * True if the code is lower case. */ -duk::Ret isLower(duk::ContextPtr ctx) +duk::Ret isLower(duk::Context *ctx) { duk::push(ctx, unicode::islower(duk::get<int>(ctx, 0))); @@ -82,7 +82,7 @@ * Returns: * True if the code is in the space category. */ -duk::Ret isSpace(duk::ContextPtr ctx) +duk::Ret isSpace(duk::Context *ctx) { duk::push(ctx, unicode::isspace(duk::get<int>(ctx, 0))); @@ -98,7 +98,7 @@ * Returns: * True if the code is title case. */ -duk::Ret isTitle(duk::ContextPtr ctx) +duk::Ret isTitle(duk::Context *ctx) { duk::push(ctx, unicode::istitle(duk::get<int>(ctx, 0))); @@ -114,7 +114,7 @@ * Returns: * True if the code is upper case. */ -duk::Ret isUpper(duk::ContextPtr ctx) +duk::Ret isUpper(duk::Context *ctx) { duk::push(ctx, unicode::isupper(duk::get<int>(ctx, 0))); @@ -143,7 +143,7 @@ duk::getGlobal<void>(plugin.context(), "Irccd"); duk::push(plugin.context(), duk::Object{}); - duk::push(plugin.context(), functions); + duk::put(plugin.context(), functions); duk::putProperty(plugin.context(), -2, "Unicode"); duk::pop(plugin.context()); }
--- a/lib/irccd/mod-util.cpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/mod-util.cpp Tue May 31 21:03:01 2016 +0200 @@ -40,20 +40,18 @@ template <> class TypeTraits<util::Substitution> { public: - static util::Substitution get(ContextPtr ctx, int index) + static util::Substitution get(duk::Context *ctx, int index) { util::Substitution params; - if (!duk::is<Object>(ctx, index)) { + if (!duk::is<Object>(ctx, index)) return params; - } - duk::enumerate(ctx, index, 0, true, [&] (ContextPtr) { - if (duk::get<std::string>(ctx, -2) == "date") { + duk::enumerate(ctx, index, 0, true, [&] (duk::Context *) { + if (duk::get<std::string>(ctx, -2) == "date") params.time = static_cast<time_t>(duk::get<double>(ctx, -1) / 1000); - } else { + else params.keywords.insert({duk::get<std::string>(ctx, -2), duk::get<std::string>(ctx, -1)}); - } }); return params; @@ -76,7 +74,7 @@ * Returns: * The converted text. */ -duk::Ret format(duk::ContextPtr ctx) +duk::Ret format(duk::Context *ctx) { try { duk::push(ctx, util::format(duk::get<std::string>(ctx, 0), duk::get<util::Substitution>(ctx, 1))); @@ -98,7 +96,7 @@ * Returns: * The nickname. */ -duk::Ret splituser(duk::ContextPtr ctx) +duk::Ret splituser(duk::Context *ctx) { const char *target = duk::require<const char *>(ctx, 0); char nick[32] = {0}; @@ -120,7 +118,7 @@ * Returns: * The hostname. */ -duk::Ret splithost(duk::ContextPtr ctx) +duk::Ret splithost(duk::Context *ctx) { const char *target = duk::require<const char *>(ctx, 0); char host[32] = {0}; @@ -150,7 +148,7 @@ duk::getGlobal<void>(plugin.context(), "Irccd"); duk::push(plugin.context(), duk::Object{}); - duk::push(plugin.context(), functions); + duk::put(plugin.context(), functions); duk::putProperty(plugin.context(), -2, "Util"); duk::pop(plugin.context()); }
--- a/lib/irccd/plugin-js.cpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/plugin-js.cpp Tue May 31 21:03:01 2016 +0200 @@ -45,14 +45,10 @@ // Call the function and discard the result. duk::insert(m_context, -nargs - 1); - if (duk::pcall(m_context, nargs) != 0) { - auto error = duk::error(m_context, -1); + if (duk::pcall(m_context, nargs) != 0) + throw duk::exception(m_context, -1, true); - duk::pop(m_context); - - throw error; - } else - duk::pop(m_context); + duk::pop(m_context); } } @@ -66,7 +62,7 @@ { duk::StackAssert sa(m_context); - duk::putGlobal(m_context, "\xff""\xff""plugin", duk::RawPointer<JsPlugin>{this}); + duk::putGlobal<void *>(m_context, "\xff""\xff""plugin", this); duk::putGlobal(m_context, "\xff""\xff""name", name()); duk::putGlobal(m_context, "\xff""\xff""path", path()); } @@ -145,7 +141,7 @@ { duk::StackAssert sa(m_context); - duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, server); duk::push(m_context, origin); duk::push(m_context, channel); duk::push(m_context, mode); @@ -161,7 +157,7 @@ { duk::StackAssert sa(m_context); - duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, server); duk::push(m_context, origin); duk::push(m_context, channel); duk::push(m_context, notice); @@ -176,7 +172,7 @@ { duk::StackAssert sa(m_context); - duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, server); duk::push(m_context, origin); duk::push(m_context, channel); duk::push(m_context, message); @@ -187,7 +183,7 @@ { duk::StackAssert sa(m_context); - duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, server); call("onConnect", 1); } @@ -195,7 +191,7 @@ { duk::StackAssert sa(m_context); - duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, server); duk::push(m_context, origin); duk::push(m_context, channel); call("onInvite", 3); @@ -205,7 +201,7 @@ { duk::StackAssert sa(m_context); - duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, server); duk::push(m_context, origin); duk::push(m_context, channel); call("onJoin", 3); @@ -220,7 +216,7 @@ { duk::StackAssert sa(m_context); - duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, server); duk::push(m_context, origin); duk::push(m_context, channel); duk::push(m_context, target); @@ -256,7 +252,7 @@ // Try to load the file (does not call onLoad yet). if (duk::pevalFile(m_context, path()) != 0) - throw duk::error(m_context, -1); + throw duk::exception(m_context, -1, true); duk::pop(m_context); @@ -286,7 +282,7 @@ { duk::StackAssert sa(m_context); - duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, server); duk::push(m_context, origin); duk::push(m_context, channel); duk::push(m_context, message); @@ -301,7 +297,7 @@ { duk::StackAssert sa(m_context); - duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, server); duk::push(m_context, origin); duk::push(m_context, channel); duk::push(m_context, message); @@ -312,7 +308,7 @@ { duk::StackAssert sa(m_context); - duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, server); duk::push(m_context, origin); duk::push(m_context, mode); call("onMode", 3); @@ -322,7 +318,7 @@ { duk::StackAssert sa(m_context); - duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, server); duk::push(m_context, channel); duk::push(m_context, names); call("onNames", 3); @@ -332,7 +328,7 @@ { duk::StackAssert sa(m_context); - duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, server); duk::push(m_context, oldnick); duk::push(m_context, newnick); call("onNick", 3); @@ -342,7 +338,7 @@ { duk::StackAssert sa(m_context); - duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, server); duk::push(m_context, origin); duk::push(m_context, notice); call("onNotice", 3); @@ -356,7 +352,7 @@ { duk::StackAssert sa(m_context); - duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, server); duk::push(m_context, origin); duk::push(m_context, channel); duk::push(m_context, reason); @@ -370,7 +366,7 @@ { duk::StackAssert sa(m_context); - duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, server); duk::push(m_context, origin); duk::push(m_context, message); call("onQuery", 3); @@ -383,7 +379,7 @@ { duk::StackAssert sa(m_context); - duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, server); duk::push(m_context, origin); duk::push(m_context, message); call("onQueryCommand", 3); @@ -404,7 +400,7 @@ { duk::StackAssert sa(m_context); - duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, server); duk::push(m_context, origin); duk::push(m_context, channel); duk::push(m_context, topic); @@ -425,7 +421,7 @@ { duk::StackAssert sa(m_context); - duk::push(m_context, duk::Shared<Server>{server}); + duk::push(m_context, server); duk::push(m_context, duk::Object{}); duk::putProperty(m_context, -1, "nickname", whois.nick); duk::putProperty(m_context, -1, "username", whois.user);
--- a/lib/irccd/plugin-js.hpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/plugin-js.hpp Tue May 31 21:03:01 2016 +0200 @@ -39,7 +39,7 @@ class JsPlugin : public Plugin { private: // JavaScript context - duk::Context m_context; + duk::UniqueContext m_context; // Store loaded modules. std::vector<std::shared_ptr<Module>> m_modules; @@ -67,7 +67,7 @@ * * \return the context */ - inline duk::Context &context() noexcept + inline duk::UniqueContext &context() noexcept { return m_context; }
--- a/lib/irccd/server-event.cpp Thu May 26 21:20:46 2016 +0200 +++ b/lib/irccd/server-event.cpp Tue May 31 21:03:01 2016 +0200 @@ -54,7 +54,7 @@ // TODO: get generic error. try { m_exec(*plugin); - } catch (const duk::ErrorInfo &info) { + } catch (const duk::Exception &info) { log::warning() << "plugin " << plugin->name() << ": error: " << info.what() << std::endl; if (!info.fileName.empty())
--- a/tests/js-elapsedtimer/main.cpp Thu May 26 21:20:46 2016 +0200 +++ b/tests/js-elapsedtimer/main.cpp Tue May 31 21:03:01 2016 +0200 @@ -46,12 +46,12 @@ { try { if (duk::pevalString(m_plugin->context(), "timer = new Irccd.ElapsedTimer();") != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); std::this_thread::sleep_for(300ms); if (duk::pevalString(m_plugin->context(), "result = timer.elapsed();") != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_GE(duk::getGlobal<int>(m_plugin->context(), "result"), 250); ASSERT_LE(duk::getGlobal<int>(m_plugin->context(), "result"), 350); @@ -64,12 +64,12 @@ { try { if (duk::pevalString(m_plugin->context(), "timer = new Irccd.ElapsedTimer();") != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); std::this_thread::sleep_for(300ms); if (duk::pevalString(m_plugin->context(), "timer.reset(); result = timer.elapsed();") != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_LE(duk::getGlobal<int>(m_plugin->context(), "result"), 100); } catch (const std::exception &ex) {
--- a/tests/js-file/main.cpp Thu May 26 21:20:46 2016 +0200 +++ b/tests/js-file/main.cpp Tue May 31 21:03:01 2016 +0200 @@ -45,7 +45,7 @@ { try { if (duk::pevalString(m_plugin->context(), "result = Irccd.File.basename('/usr/local/etc/irccd.conf');") != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_EQ("irccd.conf", duk::getGlobal<std::string>(m_plugin->context(), "result")); } catch (const std::exception &ex) { @@ -94,7 +94,7 @@ try { if (duk::pevalString(m_plugin->context(), "Irccd.File.remove('test-js-fs.remove');") != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -115,7 +115,7 @@ ); if (ret != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_EQ("file-1.txt", duk::getGlobal<std::string>(m_plugin->context(),"result")); } catch (const std::exception &ex) { @@ -135,7 +135,7 @@ ); if (ret != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_EQ("file-1.txt", duk::getGlobal<std::string>(m_plugin->context(),"result")); } catch (const std::exception &ex) { @@ -154,7 +154,7 @@ ); if (ret != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_EQ(std::string{IRCCD_TESTS_DIRECTORY "/level-1"}, duk::getGlobal<std::string>(m_plugin->context(), "result")); } catch (const std::exception &ex) { @@ -174,7 +174,7 @@ ); if (ret != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_EQ(std::string{IRCCD_TESTS_DIRECTORY "/level-1"}, duk::getGlobal<std::string>(m_plugin->context(), "result")); } catch (const std::exception &ex) { @@ -192,7 +192,7 @@ ); if (ret != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); std::vector<std::string> expected{"a", "b", "c"}; @@ -214,7 +214,7 @@ ); if (ret != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_EQ(".", duk::getGlobal<std::string>(m_plugin->context(), "result")); } catch (const std::exception &ex) { @@ -236,7 +236,7 @@ ); if (ret != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_TRUE(duk::getGlobal<bool>(m_plugin->context(), "result")); } catch (const std::exception &ex) { @@ -257,7 +257,7 @@ ); if (ret != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_EQ(".", duk::getGlobal<std::string>(m_plugin->context(), "result")); } catch (const std::exception &ex) { @@ -280,7 +280,7 @@ ); if (ret != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_TRUE(duk::getGlobal<bool>(m_plugin->context(), "result")); } catch (const std::exception &ex) { @@ -300,7 +300,7 @@ ); if (ret != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_EQ("x", duk::getGlobal<std::string>(m_plugin->context(), "result")); } catch (const std::exception &ex) { @@ -322,7 +322,7 @@ ); if (ret != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_TRUE(duk::getGlobal<bool>(m_plugin->context(), "result")); } catch (const std::exception &ex) { @@ -344,7 +344,7 @@ ); if (ret != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); std::vector<std::string> expected{"a", "b", "c"}; @@ -369,7 +369,7 @@ ); if (ret != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); std::vector<std::string> expected;
--- a/tests/js-irccd/main.cpp Thu May 26 21:20:46 2016 +0200 +++ b/tests/js-irccd/main.cpp Tue May 31 21:03:01 2016 +0200 @@ -48,7 +48,7 @@ ); if (ret != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_EQ(IRCCD_VERSION_MAJOR, duk::getGlobal<int>(m_plugin->context(), "major")); ASSERT_EQ(IRCCD_VERSION_MINOR, duk::getGlobal<int>(m_plugin->context(), "minor")); @@ -74,7 +74,7 @@ ); if (ret != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_EQ(1, duk::getGlobal<int>(m_plugin->context(), "errno")); ASSERT_EQ("SystemError", duk::getGlobal<std::string>(m_plugin->context(), "name")); @@ -89,7 +89,7 @@ TEST_F(TestJsIrccd, fromNative) { try { - duk::push(m_plugin->context(), duk::Function{[] (duk::ContextPtr ctx) -> duk::Ret { + duk::push(m_plugin->context(), duk::Function{[] (duk::Context *ctx) -> duk::Ret { duk::raise(ctx, SystemError{EINVAL, "hey"}); return 0; @@ -110,7 +110,7 @@ ); if (ret != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_EQ(EINVAL, duk::getGlobal<int>(m_plugin->context(), "errno")); ASSERT_EQ("SystemError", duk::getGlobal<std::string>(m_plugin->context(), "name"));
--- a/tests/js-logger/main.cpp Thu May 26 21:20:46 2016 +0200 +++ b/tests/js-logger/main.cpp Tue May 31 21:03:01 2016 +0200 @@ -73,7 +73,7 @@ duk::putGlobal(m_plugin->context(), "\xff""\xff""name", "test"); if (duk::pevalString(m_plugin->context(), "Irccd.Logger.info(\"hello!\");") != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_EQ("plugin test: hello!", lineInfo); } catch (const std::exception &ex) { @@ -87,7 +87,7 @@ duk::putGlobal(m_plugin->context(), "\xff""\xff""name", "test"); if (duk::pevalString(m_plugin->context(), "Irccd.Logger.warning(\"FAIL!\");") != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_EQ("plugin test: FAIL!", lineWarning); } catch (const std::exception &ex) { @@ -103,7 +103,7 @@ duk::putGlobal(m_plugin->context(), "\xff""\xff""name", "test"); if (duk::pevalString(m_plugin->context(), "Irccd.Logger.debug(\"starting\");") != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_EQ("plugin test: starting", lineDebug); } catch (const std::exception &ex) {
--- a/tests/js-system/CMakeLists.txt Thu May 26 21:20:46 2016 +0200 +++ b/tests/js-system/CMakeLists.txt Tue May 31 21:03:01 2016 +0200 @@ -20,5 +20,6 @@ NAME js-system SOURCES main.cpp LIBRARIES libirccd + FLAGS IRCCD_EXECUTABLE=\"$<TARGET_FILE:irccd>\" )
--- a/tests/js-system/main.cpp Thu May 26 21:20:46 2016 +0200 +++ b/tests/js-system/main.cpp Tue May 31 21:03:01 2016 +0200 @@ -23,6 +23,7 @@ #include <irccd/mod-system.hpp> #include <irccd/plugin-js.hpp> #include <irccd/service-module.hpp> +#include <irccd/sysconfig.hpp> #include <irccd/system.hpp> using namespace irccd; @@ -36,6 +37,7 @@ : m_plugin(std::make_shared<JsPlugin>("empty", SOURCEDIR "/empty.js")) { m_irccd.moduleService().get("Irccd")->load(m_irccd, *m_plugin); + m_irccd.moduleService().get("Irccd.File")->load(m_irccd, *m_plugin); m_irccd.moduleService().get("Irccd.System")->load(m_irccd, *m_plugin); } }; @@ -51,6 +53,27 @@ } } +#if defined(HAVE_POPEN) + +TEST_F(TestJsSystem, popen) +{ + try { + auto ret = duk::pevalString(m_plugin->context(), + "f = Irccd.System.popen(\"" IRCCD_EXECUTABLE " --version\", \"r\");" + "r = f.readline();" + ); + + if (ret != 0) + throw duk::exception(m_plugin->context(), -1); + + ASSERT_EQ(IRCCD_VERSION, duk::getGlobal<std::string>(m_plugin->context(), "r")); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +#endif + int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv);
--- a/tests/js-util/main.cpp Thu May 26 21:20:46 2016 +0200 +++ b/tests/js-util/main.cpp Tue May 31 21:03:01 2016 +0200 @@ -48,7 +48,7 @@ ); if (ret != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); } catch (const std::exception &ex) { FAIL() << ex.what(); } @@ -58,7 +58,7 @@ { try { if (duk::pevalString(m_plugin->context(), "result = Irccd.Util.splituser(\"user!~user@hyper/super/host\");") != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_EQ("user", duk::getGlobal<std::string>(m_plugin->context(), "result")); } catch (const std::exception &ex) { @@ -70,7 +70,7 @@ { try { if (duk::pevalString(m_plugin->context(), "result = Irccd.Util.splithost(\"user!~user@hyper/super/host\");") != 0) - throw duk::error(m_plugin->context(), -1); + throw duk::exception(m_plugin->context(), -1); ASSERT_EQ("!~user@hyper/super/host", duk::getGlobal<std::string>(m_plugin->context(), "result")); } catch (const std::exception &ex) {