Mercurial > code
changeset 434:da92b98de599
Merge branch 'master' of malikania.fr:code
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 21 Oct 2015 21:24:13 +0200 |
parents | 7f01d500460a (current diff) 70bcc6962c88 (diff) |
children | b9c8b1792889 |
files | C++/modules/Socket/Socket.cpp C++/modules/Socket/Socket.h |
diffstat | 6 files changed, 2252 insertions(+), 2215 deletions(-) [+] |
line wrap: on
line diff
--- a/C++/modules/Ini/Ini.cpp Wed Oct 21 21:24:04 2015 +0200 +++ b/C++/modules/Ini/Ini.cpp Wed Oct 21 21:24:13 2015 +0200 @@ -18,6 +18,7 @@ #include <cassert> #include <cctype> +#include <cstring> #include <iostream> #include <iterator> #include <fstream> @@ -391,6 +392,11 @@ Tokens Document::analyze(const File &file) { std::fstream stream{file.path}; + + if (!stream) { + throw std::runtime_error{std::strerror(errno)}; + } + std::istreambuf_iterator<char> it{stream}; std::istreambuf_iterator<char> end{};
--- a/C++/modules/Ini/Ini.h Wed Oct 21 21:24:04 2015 +0200 +++ b/C++/modules/Ini/Ini.h Wed Oct 21 21:24:13 2015 +0200 @@ -55,10 +55,10 @@ * @param c the column * @param m the message */ - inline Error(int l, int c, std::string m) noexcept - : m_line{l} - , m_column{c} - , m_message{std::move(m)} + inline Error(int line, int column, std::string message) noexcept + : m_line{line} + , m_column{column} + , m_message{std::move(message)} { } @@ -372,14 +372,6 @@ * Path to the file. */ std::string path; - - /** - * Load the file into the document. - * - * @param doc the document - * @throw Error on errors - */ - void load(Document &doc); }; /** @@ -393,14 +385,6 @@ * The ini content. */ std::string text; - - /** - * Load the file into the document. - * - * @param doc the document - * @throw Error on errors - */ - void load(Document &doc); }; /**
--- a/C++/modules/Js/Js.cpp Wed Oct 21 21:24:04 2015 +0200 +++ b/C++/modules/Js/Js.cpp Wed Oct 21 21:24:13 2015 +0200 @@ -1,107 +1,107 @@ -/* - * Js.cpp -- JavaScript C++14 wrapper for Duktape - * - * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "Js.h" - -using namespace std::string_literals; - -namespace js { - -ErrorInfo Context::error(int index) -{ - ErrorInfo error; - - index = duk_normalize_index(m_handle.get(), index); - - duk_get_prop_string(m_handle.get(), index, "name"); - error.name = duk_to_string(m_handle.get(), -1); - duk_get_prop_string(m_handle.get(), index, "message"); - error.message = duk_to_string(m_handle.get(), -1); - duk_get_prop_string(m_handle.get(), index, "fileName"); - error.fileName = duk_to_string(m_handle.get(), -1); - duk_get_prop_string(m_handle.get(), index, "lineNumber"); - error.lineNumber = duk_to_int(m_handle.get(), -1); - duk_get_prop_string(m_handle.get(), index, "stack"); - error.stack = duk_to_string(m_handle.get(), -1); - duk_pop_n(m_handle.get(), 5); - - return error; -} - -void Context::pcall(unsigned nargs) -{ - if (duk_pcall(m_handle.get(), nargs) != 0) { - ErrorInfo info = error(-1); - duk_pop(m_handle.get()); - - throw info; - } -} - -void Context::peval() -{ - if (duk_peval(m_handle.get()) != 0) { - ErrorInfo info = error(-1); - duk_pop(m_handle.get()); - - throw info; - } -} - -void TypeInfo<Function>::push(Context &ctx, Function fn) -{ - /* 1. Push function wrapper */ - duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t { - Context context{ctx}; - - duk_push_current_function(ctx); - duk_get_prop_string(ctx, -1, "\xff""\xff""js-func"); - Function *f = static_cast<Function *>(duk_to_pointer(ctx, -1)); - duk_pop_2(ctx); - - return static_cast<duk_ret_t>(f->function(context)); - }, fn.nargs); - - /* 2. Store the moved function */ - duk_push_pointer(ctx, new Function(std::move(fn))); - duk_put_prop_string(ctx, -2, "\xff""\xff""js-func"); - - /* 3. Store deletion flags */ - duk_push_boolean(ctx, false); - duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted"); - - /* 4. Push and set a finalizer */ - duk_push_c_function(ctx, [] (duk_context *ctx) { - duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted"); - - if (duk_to_boolean(ctx, -1)) { - duk_push_boolean(ctx, true); - duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted"); - duk_get_prop_string(ctx, 0, "\xff""\xff""js-func"); - delete static_cast<Function *>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - } - - duk_pop(ctx); - - return 0; - }, 1); - duk_set_finalizer(ctx, -2); -} - -} // !js +/* + * Js.cpp -- JavaScript C++14 wrapper for Duktape + * + * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "Js.h" + +using namespace std::string_literals; + +namespace js { + +ErrorInfo Context::error(int index) +{ + ErrorInfo error; + + index = duk_normalize_index(m_handle.get(), index); + + duk_get_prop_string(m_handle.get(), index, "name"); + error.name = duk_to_string(m_handle.get(), -1); + duk_get_prop_string(m_handle.get(), index, "message"); + error.message = duk_to_string(m_handle.get(), -1); + duk_get_prop_string(m_handle.get(), index, "fileName"); + error.fileName = duk_to_string(m_handle.get(), -1); + duk_get_prop_string(m_handle.get(), index, "lineNumber"); + error.lineNumber = duk_to_int(m_handle.get(), -1); + duk_get_prop_string(m_handle.get(), index, "stack"); + error.stack = duk_to_string(m_handle.get(), -1); + duk_pop_n(m_handle.get(), 5); + + return error; +} + +void Context::pcall(unsigned nargs) +{ + if (duk_pcall(m_handle.get(), nargs) != 0) { + ErrorInfo info = error(-1); + duk_pop(m_handle.get()); + + throw info; + } +} + +void Context::peval() +{ + if (duk_peval(m_handle.get()) != 0) { + ErrorInfo info = error(-1); + duk_pop(m_handle.get()); + + throw info; + } +} + +void TypeInfo<Function>::push(Context &ctx, Function fn) +{ + /* 1. Push function wrapper */ + duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t { + Context context{ctx}; + + duk_push_current_function(ctx); + duk_get_prop_string(ctx, -1, "\xff""\xff""js-func"); + Function *f = static_cast<Function *>(duk_to_pointer(ctx, -1)); + duk_pop_2(ctx); + + return static_cast<duk_ret_t>(f->function(context)); + }, fn.nargs); + + /* 2. Store the moved function */ + duk_push_pointer(ctx, new Function(std::move(fn))); + duk_put_prop_string(ctx, -2, "\xff""\xff""js-func"); + + /* 3. Store deletion flags */ + duk_push_boolean(ctx, false); + duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted"); + + /* 4. Push and set a finalizer */ + duk_push_c_function(ctx, [] (duk_context *ctx) { + duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted"); + + if (!duk_to_boolean(ctx, -1)) { + duk_push_boolean(ctx, true); + duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted"); + duk_get_prop_string(ctx, 0, "\xff""\xff""js-func"); + delete static_cast<Function *>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + } + + duk_pop(ctx); + + return 0; + }, 1); + duk_set_finalizer(ctx, -2); +} + +} // !js
--- a/C++/modules/Js/Js.h Wed Oct 21 21:24:04 2015 +0200 +++ b/C++/modules/Js/Js.h Wed Oct 21 21:24:13 2015 +0200 @@ -1,2067 +1,2067 @@ -/* - * Js.h -- JavaScript C++14 wrapper for Duktape - * - * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _JS_H_ -#define _JS_H_ - -/** - * @file Js.h - * @brief Bring JavaScript using Duktape - * - * This file provides usual Duktape function renamed and placed into `js` namespace. It also replaces error - * code with exceptions when possible. - * - * For convenience, this file also provides templated functions, overloads and much more. - */ - -#include <functional> -#include <memory> -#include <string> -#include <type_traits> -#include <unordered_map> -#include <utility> -#include <vector> - -#include <duktape.h> - -/** - * Duktape C++ namespace wrapper. - */ -namespace js { - -class Context; - -/** - * Typedef for readability. - */ -using ContextPtr = duk_context *; - -/* - * Basic types to manipulate with the stack - * ------------------------------------------------------------------ - * - * The following types can be used in some of the operations like Context::push or Context::is, they are defined - * usually as empty classes to determine the appropriate action to execute. - * - * For example, `ctx.push(js::Object{})` will push an empty object into the stack. - */ - -/** - * @class Object - * @brief Empty class tag for push() function. - */ -class Object { -}; - -/** - * @class Array - * @brief Empty class tag for push() function. - */ -class Array { -}; - -/** - * @class Global - * @brief Empty class tag to push the global object. - */ -class Global { -}; - -/** - * @class Undefined - * @brief Empty class tag to push undefined to the stack. - */ -class Undefined { -}; - -/** - * @class Null - * @brief Empty class tag to push null to the stack. - */ -class Null { -}; - -/** - * @class This - * @brief Empty class tag to push this binding to the stack. - */ -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; -}; - -/* - * Extended type manipulation - * ------------------------------------------------------------------ - * - * The following types are different as there are no equivalent in the native Duktape API, they are available for - * convenience. - */ - -/** - * @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. - * - * The only requirement is to have the function `void prototype(Context &ctx)` in your class T. - */ -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}; -}; - -/** - * @class Function - * @brief Duktape/C function definition. - * - * This class wraps the std::function as a Duktape/C function by storing a copied pointer. - */ -class Function { -public: - /** - * The function pointer, must not be null. - */ - std::function<int (Context &)> function; - - /** - * Number of args that the function takes - */ - int nargs{0}; -}; - -/** - * Map of functions to set on an object. - */ -using FunctionMap = std::unordered_map<std::string, Function>; - -/** - * Map of string to type, ideal for setting constants like enums. - */ -template <typename Type> -using Map = std::unordered_map<std::string, Type>; - -/** - * @class ErrorInfo - * @brief Error description. - * - * This class fills the fields got in an Error object. - */ -class ErrorInfo : public std::exception { -public: - std::string name; //!< name of error - std::string message; //!< error message - std::string stack; //!< stack if available - std::string fileName; //!< filename if applicable - int lineNumber{0}; //!< line number if applicable - - /** - * Get the error message. This effectively returns message field. - * - * @return the message - */ - const char *what() const noexcept override - { - return message.c_str(); - } -}; - -/** - * @class TypeInfo - * @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 TypeInfo { -}; - -/** - * @class File - * @brief Evaluate script from file. - * @see Context::eval - * @see Context::peval - */ -class File { -public: - /** - * Path to the file. - */ - std::string path; - - /** - * Evaluate the file. - * - * @param ctx the context - */ - inline void eval(duk_context *ctx) - { - duk_eval_file(ctx, path.c_str()); - } - - /** - * Evaluate in protected mode the file. - * - * @param ctx the context - */ - inline int peval(duk_context *ctx) - { - return duk_peval_file(ctx, path.c_str()); - } -}; - -/** - * @class Script - * @brief Evaluate script from raw text. - * @see Context::eval - * @see Context::peval - */ -class Script { -public: - /** - * The script content. - */ - std::string text; - - /** - * Evaluate the script. - * - * @param ctx the context - */ - inline void eval(duk_context *ctx) - { - duk_eval_string(ctx, text.c_str()); - } - - /** - * Evaluate in protected mode the script. - * - * @param ctx the context - */ - inline int peval(duk_context *ctx) - { - return duk_peval_string(ctx, text.c_str()); - } -}; - -/** - * @class Context - * @brief RAII based Duktape handler. - * - * This class is implicitly convertible to duk_context for convenience. - */ -class Context { -private: - using Deleter = void (*)(duk_context *); - using Handle = std::unique_ptr<duk_context, Deleter>; - - Handle m_handle; - - /* Move and copy forbidden */ - Context(const Context &) = delete; - Context &operator=(const Context &) = delete; - Context(const Context &&) = delete; - Context &operator=(const Context &&) = delete; - -public: - /** - * Create default context. - */ - inline Context() - : m_handle{duk_create_heap_default(), duk_destroy_heap} - { - } - - /** - * Create borrowed context that will not be deleted. - * - * @param ctx the pointer to duk_context - */ - inline Context(ContextPtr ctx) noexcept - : m_handle{ctx, [] (ContextPtr) {}} - { - } - - /** - * Convert the context to the native Duktape/C type. - * - * @return the duk_context - */ - inline operator duk_context *() noexcept - { - return m_handle.get(); - } - - /** - * Convert the context to the native Duktape/C type. - * - * @return the duk_context - */ - inline operator duk_context *() const noexcept - { - return m_handle.get(); - } - - /* - * Basic functions - * ---------------------------------------------------------- - * - * The following functions are just standard wrappers around the native Duktape C functions, they are - * defined with the same signature except that for convenience some parameters have default sane values. - */ - - /** - * Call the object at the top of the stack. - * - * @param ctx the context - * @param nargs the number of arguments - * @note Non-protected - */ - inline void call(unsigned nargs = 0) - { - duk_call(m_handle.get(), nargs); - } - - /** - * Copy a value from from to to, overwriting the previous value. If either index is invalid, throws an error. - * - * @param from the from index - * @param to the destination - */ - inline void copy(int from, int to) - { - duk_copy(m_handle.get(), from, to); - } - - /** - * Define a property. - * - * @param index the object index - * @param flags the flags - * @note Wrapper of duk_def_prop - */ - inline void defineProperty(int index, int flags) - { - duk_def_prop(m_handle.get(), index, flags); - } - - /** - * Delete a property. - * - * @param index the object index - * @return true if deleted - * @note Wrapper of duk_del_prop - */ - inline bool deleteProperty(int index) - { - return duk_del_prop(m_handle.get(), index); - } - - /** - * Delete a property by index. - * - * @param index the object index - * @param position the property index - * @return true if deleted - * @note Wrapper of duk_del_prop_index - */ - inline bool deleteProperty(int index, int position) - { - return duk_del_prop_index(m_handle.get(), index, position); - } - - /** - * Delete a property by name. - * - * @param index the object index - * @param name the property name - * @return true if deleted - * @note Wrapper of duk_del_prop_string - */ - inline bool deleteProperty(int index, const std::string &name) - { - return duk_del_prop_string(m_handle.get(), index, name.c_str()); - } - - /** - * Push a duplicate of value at from_index to the stack. If from_index is invalid, throws an error. - * - * @param index the value to copy - * @note Wrapper of duk_dup - */ - inline void dup(int index = -1) - { - duk_dup(m_handle.get(), index); - } - - /** - * Evaluate a non-protected chunk that is at the top of the stack. - */ - inline void eval() - { - duk_eval(m_handle.get()); - } - - /** - * Check if the object as a property. - * - * @param index the object index - * @return true if has - * @note Wrapper of duk_has_prop - */ - inline bool hasProperty(int index) - { - return duk_has_prop(m_handle.get(), index); - } - - /** - * Check if the object as a property by index. - * - * @param index the object index - * @param position the property index - * @return true if has - * @note Wrapper of duk_has_prop_index - */ - inline bool hasProperty(int index, int position) - { - return duk_has_prop_index(m_handle.get(), index, position); - } - - /** - * Check if the object as a property by string - * - * @param index the object index - * @param name the property name - * @return true if has - * @note Wrapper of duk_has_prop_string - */ - inline bool hasProperty(int index, const std::string &name) - { - return duk_has_prop_string(m_handle.get(), index, name.c_str()); - } - - /** - * Check if idx1 is an instance of idx2. - * - * @param ctx the context - * @param idx1 the value to test - * @param idx2 the instance requested - * @return true if idx1 is instance of idx2 - * @note Wrapper of duk_instanceof - */ - inline bool instanceof(int idx1, int idx2) - { - return duk_instanceof(m_handle.get(), idx1, idx2); - } - - /** - * Insert a value at to with a value popped from the stack top. The previous value at to and any values above - * it are moved up the stack by a step. If to is an invalid index, throws an error. - * - * @note Negative indices are evaluated prior to popping the value at the stack top - * @param to the destination - * @note Wrapper of duk_insert - */ - inline void insert(int to) - { - duk_insert(m_handle.get(), to); - } - - /** - * Pop a certain number of values from the top of the stack. - * - * @param ctx the context - * @param count the number of values to pop - * @note Wrapper of duk_pop_n - */ - inline void pop(unsigned count = 1) - { - duk_pop_n(m_handle.get(), count); - } - - /** - * Remove value at index. Elements above index are shifted down the stack by a step. If to is an invalid index, - * throws an error. - * - * @param index the value to remove - * @note Wrapper of duk_remove - */ - inline void remove(int index) - { - duk_remove(m_handle.get(), index); - } - - /** - * Replace value at to_index with a value popped from the stack top. If to_index is an invalid index, - * throws an error. - * - * @param index the value to replace by the value at the top of the stack - * @note Negative indices are evaluated prior to popping the value at the stack top. - * @note Wrapper of duk_replace - */ - inline void replace(int index) - { - duk_replace(m_handle.get(), index); - } - - /** - * Swap values at indices index1 and index2. If the indices are the same, the call is a no-op. If either index - * is invalid, throws an error. - * - * @param index1 the first index - * @param index2 the second index - * @note Wrapper of duk_swap - */ - inline void swap(int index1, int index2) - { - duk_swap(m_handle.get(), index1, index2); - } - - /** - * Get the current stack size. - * - * @param ctx the context - * @return the stack size - * @note Wrapper of duk_get_top - */ - inline int top() noexcept - { - return duk_get_top(m_handle.get()); - } - - /** - * Get the type of the value at the specified index. - * - * @param ctx the context - * @param index the idnex - * @return the type - * @note Wrapper of duk_get_type - */ - inline int type(int index) noexcept - { - return duk_get_type(m_handle.get(), index); - } - - /* - * Extended native functions - * ---------------------------------------------------------- - * - * The following functions have different behaviour than the original native Duktape C functions, see their - * descriptions for more information - */ - - /** - * Call in protected mode the object at the top of the stack. - * - * @param nargs the number of arguments - * @throw ErrorInfo on errors - * @note Wrapper of duk_pcall - */ - void pcall(unsigned nargs = 0); - - /** - * Evaluate a non-protected source. - * - * @param source the source - * @see File - * @see Script - * @note Wrapper of duk_eval - */ - template <typename Source> - inline void eval(Source &&source) - { - source.eval(m_handle.get()); - } - - /** - * Evaluate a protected chunk that is at the top of the stack. - * - * @throw ErrorInfo the error - * @note Wrapper of duk_peval - */ - void peval(); - - /** - * Evaluate a protected source. - * - * @param source the source - * @see File - * @see Script - * @throw ErrorInfo on failure - * @note Wrapper of duk_peval - */ - template <typename Source> - inline void peval(Source &&source) - { - if (source.peval(m_handle.get()) != 0) { - ErrorInfo info = error(-1); - duk_pop(m_handle.get()); - - throw info; - } - } - - /* - * Push / Get / Require / Is / Optional - * ---------------------------------------------------------- - * - * The following functions are used to push, get or check values from the stack. They use specialization - * of TypeInfo class. - */ - - /** - * Push a value into the stack. Calls TypeInfo<T>::push(*this, value); - * - * @param value the value to forward - */ - template <typename Type> - inline void push(Type &&value) - { - TypeInfo<std::decay_t<Type>>::push(*this, std::forward<Type>(value)); - } - - /** - * Generic template function to get a value from the stack. - * - * @param index the index - * @return the value - */ - template <typename Type> - inline auto get(int index) -> decltype(TypeInfo<Type>::get(*this, 0)) - { - return TypeInfo<Type>::get(*this, index); - } - - /** - * Require a type at the specified index. - * - * @param index the index - * @return the value - */ - template <typename Type> - inline auto require(int index) -> decltype(TypeInfo<Type>::require(*this, 0)) - { - return TypeInfo<Type>::require(*this, index); - } - - /** - * Check if a value is a type of T. - * - * The TypeInfo<T> must have `static bool is(ContextPtr ptr, int index)`. - * - * @param index the value index - * @return true if is the type - */ - template <typename T> - inline bool is(int index) - { - return TypeInfo<T>::is(*this, index); - } - - /** - * Get an optional value from the stack, if the value is not available of not the correct type, - * return defaultValue instead. - * - * The TypeInfo<T> must have `static T optional(Context &, int index, T &&defaultValue)`. - * - * @param index the value index - * @param defaultValue the value replacement - * @return the value or defaultValue - */ - template <typename Type> - inline auto optional(int index, Type &&defaultValue) - { - return TypeInfo<std::decay_t<Type>>::optional(*this, index, std::forward<Type>(defaultValue)); - } - - /* - * Properties management - * ---------------------------------------------------------- - * - * The following functions are used to read or set properties on objects or globals also using TypeInfo. - */ - - /** - * Get the property `name' as value from the object at the specified index. - * - * @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(int index, const std::string &name) -> decltype(get<Type>(0)) - { - duk_get_prop_string(m_handle.get(), index, name.c_str()); - decltype(get<Type>(0)) value = get<Type>(-1); - duk_pop(m_handle.get()); - - return value; - } - - /** - * Get a property by index, for arrays. - * - * @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(int index, int position) -> decltype(get<Type>(0)) - { - duk_get_prop_index(m_handle.get(), index, position); - decltype(get<Type>(0)) value = get<Type>(-1); - duk_pop(m_handle.get()); - - return value; - } - - /** - * Get the property `name' and push it to the stack from the object at the specified index. - * - * @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(int index, const std::string &name) - { - duk_get_prop_string(m_handle.get(), index, name.c_str()); - } - - /** - * Get the property by index and push it to the stack from the object at the specified index. - * - * @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(int index, int position) - { - duk_get_prop_index(m_handle.get(), index, position); - } - - /** - * Set a property to the object at the specified index. - * - * @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(int index, const std::string &name, Type &&value) - { - index = duk_normalize_index(m_handle.get(), index); - - push(std::forward<Type>(value)); - duk_put_prop_string(m_handle.get(), index, name.c_str()); - } - - /** - * Set a property by index, for arrays. - * - * @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(int index, int position, Type &&value) - { - index = duk_normalize_index(m_handle.get(), index); - - push(std::forward<Type>(value)); - duk_put_prop_index(m_handle.get(), index, position); - } - - /** - * 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(int index, const std::string &name) - { - duk_put_prop_string(m_handle.get(), 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(int index, int position) - { - duk_put_prop_index(m_handle.get(), index, position); - } - - /** - * Get a global value. - * - * @param name the name of the global variable - * @return the value - */ - template <typename Type> - inline auto getGlobal(const std::string &name, std::enable_if_t<!std::is_void<Type>::value> * = nullptr) -> decltype(get<Type>(0)) - { - duk_get_global_string(m_handle.get(), name.c_str()); - decltype(get<Type>(0)) value = get<Type>(-1); - duk_pop(m_handle.get()); - - return value; - } - - /** - * Overload that push the value at the top of the stack instead of returning it. - */ - template <typename Type> - inline void getGlobal(const std::string &name, std::enable_if_t<std::is_void<Type>::value> * = nullptr) noexcept - { - duk_get_global_string(m_handle.get(), name.c_str()); - } - - /** - * Set a global variable. - * - * @param name the name of the global variable - * @param type the value to set - */ - template <typename Type> - inline void putGlobal(const std::string &name, Type&& type) - { - push(std::forward<Type>(type)); - duk_put_global_string(m_handle.get(), name.c_str()); - } - - /** - * Put the value at the top of the stack as global property. - * - * @param name the property name - */ - inline void putGlobal(const std::string &name) - { - duk_put_global_string(m_handle.get(), name.c_str()); - } - - /* - * Extra functions - * ---------------------------------------------------------- - * - * The following functions are implemented for convenience and do not exists in the native Duktape API. - */ - - /** - * Get the error object when a JavaScript error has been thrown (e.g. eval failure). - * - * @param index the index - * @return the information - */ - ErrorInfo error(int index); - - /** - * Enumerate an object or an array at the specified index. - * - * @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(int index, duk_uint_t flags, duk_bool_t getvalue, Func&& func) - { - duk_enum(m_handle.get(), index, flags); - - while (duk_next(m_handle.get(), -1, getvalue)) { - func(*this); - duk_pop_n(m_handle.get(), 1 + (getvalue ? 1 : 0)); - } - - duk_pop(m_handle.get()); - } - - /** - * Return the this binding of the current function. - * - * @return the this binding as the template given - */ - template <typename T> - inline auto self() -> decltype(TypeInfo<T>::get(*this, 0)) - { - duk_push_this(m_handle.get()); - decltype(TypeInfo<T>::get(*this, 0)) value = TypeInfo<T>::get(*this, -1); - duk_pop(m_handle.get()); - - return value; - } - - /** - * Throw an ECMAScript exception. - * - * @param ex the exception - */ - template <typename Exception> - void raise(const Exception &ex) - { - ex.create(m_handle.get()); - - duk_push_string(m_handle.get(), ex.name().c_str()); - duk_put_prop_string(m_handle.get(), -2, "name"); - duk_throw(m_handle.get()); - } - - /** - * Construct the object in place, setting value as this binding. - * - * The TypeInfo<T> must have the following requirements: - * - * - static void construct(Context &, T): must update this with the value and keep the stack unchanged - * - * @param value the value to forward - * @see self - */ - template <typename T> - inline void construct(T &&value) - { - TypeInfo<std::decay_t<T>>::construct(*this, std::forward<T>(value)); - } -}; - -/* ------------------------------------------------------------------ - * Exception handling - * ------------------------------------------------------------------ */ - -/** - * @class Error - * @brief Base ECMAScript error class. - * @warning Override the function create for your own exceptions - */ -class Error { -protected: - std::string m_name; //!< Name of exception (e.g RangeError) - std::string m_message; //!< The message - - /** - * Constructor with a type of error specified, specially designed for derived errors. - * - * @param name the error name (e.g RangeError) - * @param message the message - */ - inline Error(std::string name, std::string message) noexcept - : m_name{std::move(name)} - , m_message{std::move(message)} - { - } - -public: - /** - * Constructor with a message. - * - * @param message the message - */ - inline Error(std::string message) noexcept - : m_name{"Error"} - , m_message{std::move(message)} - { - } - - /** - * Get the error type (e.g RangeError). - * - * @return the name - */ - inline const std::string &name() const noexcept - { - return m_name; - } - - /** - * Create the exception on the stack. - * - * @note the default implementation search for the global variables - * @param ctx the context - */ - virtual void create(ContextPtr ctx) const noexcept - { - duk_get_global_string(ctx, m_name.c_str()); - duk_push_string(ctx, m_message.c_str()); - duk_new(ctx, 1); - } -}; - -/** - * @class EvalError - * @brief Error in eval() function. - */ -class EvalError : public Error { -public: - /** - * Construct an EvalError. - * - * @param message the message - */ - inline EvalError(std::string message) noexcept - : Error{"EvalError", std::move(message)} - { - } -}; - -/** - * @class RangeError - * @brief Value is out of range. - */ -class RangeError : public Error { -public: - /** - * Construct an RangeError. - * - * @param message the message - */ - inline RangeError(std::string message) noexcept - : Error{"RangeError", std::move(message)} - { - } -}; - -/** - * @class ReferenceError - * @brief Trying to use a variable that does not exist. - */ -class ReferenceError : public Error { -public: - /** - * Construct an ReferenceError. - * - * @param message the message - */ - inline ReferenceError(std::string message) noexcept - : Error{"ReferenceError", std::move(message)} - { - } -}; - -/** - * @class SyntaxError - * @brief Syntax error in the script. - */ -class SyntaxError : public Error { -public: - /** - * Construct an SyntaxError. - * - * @param message the message - */ - inline SyntaxError(std::string message) noexcept - : Error{"SyntaxError", std::move(message)} - { - } -}; - -/** - * @class TypeError - * @brief Invalid type given. - */ -class TypeError : public Error { -public: - /** - * Construct an TypeError. - * - * @param message the message - */ - inline TypeError(std::string message) noexcept - : Error{"TypeError", std::move(message)} - { - } -}; - -/** - * @class URIError - * @brief URI manipulation failure. - */ -class URIError : public Error { -public: - /** - * Construct an URIError. - * - * @param message the message - */ - inline URIError(std::string message) noexcept - : Error{"URIError", std::move(message)} - { - } -}; - -/* ------------------------------------------------------------------ - * Standard overloads for TypeInfo<T> - * ------------------------------------------------------------------ */ - -/** - * @class TypeInfo<int> - * @brief Default implementation for int. - * - * Provides: get, is, optional, push, require. - */ -template <> -class TypeInfo<int> { -public: - /** - * Get an integer, return 0 if not an integer. - * - * @param ctx the context - * @param index the index - * @return the integer - */ - static inline int get(Context &ctx, int index) - { - return duk_get_int(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); - } - - /** - * 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 int optional(Context &ctx, int index, int defaultValue) - { - if (!duk_is_number(ctx, index)) { - return defaultValue; - } - - return duk_get_int(ctx, index); - } - - /** - * Push an integer. - * - * @param ctx the context - * @param value the value - */ - static inline void push(Context &ctx, int value) - { - duk_push_int(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 int require(Context &ctx, int index) - { - return duk_require_int(ctx, index); - } -}; - -/** - * @class TypeInfo<bool> - * @brief Default implementation for bool. - * - * Provides: get, is, optional, push, require. - */ -template <> -class TypeInfo<bool> { -public: - /** - * Get a boolean, return 0 if not a boolean. - * - * @param ctx the context - * @param index the index - * @return the boolean - */ - static inline bool get(Context &ctx, int index) - { - return duk_get_boolean(ctx, index); - } - - /** - * Check if value is a boolean. - * - * @param ctx the context - * @param index the index - * @return true if boolean - */ - static inline bool is(Context &ctx, int index) - { - return duk_is_boolean(ctx, index); - } - - /** - * Get a bool, return defaultValue if the value is not a boolean. - * - * @param ctx the context - * @param index the index - * @param defaultValue the defaultValue - * @return the boolean or defaultValue - */ - static inline bool optional(Context &ctx, int index, bool defaultValue) - { - if (!duk_is_boolean(ctx, index)) { - return defaultValue; - } - - return duk_get_boolean(ctx, index); - } - - /** - * Push a boolean. - * - * @param ctx the context - * @param value the value - */ - static inline void push(Context &ctx, bool value) - { - duk_push_boolean(ctx, value); - } - - /** - * Require a boolean, throws a JavaScript exception if not a boolean. - * - * @param ctx the context - * @param index the index - * @return the boolean - */ - static inline bool require(Context &ctx, int index) - { - return duk_require_boolean(ctx, index); - } -}; - -/** - * @class TypeInfo<double> - * @brief Default implementation for double. - * - * Provides: get, is, optional, push, require. - */ -template <> -class TypeInfo<double> { -public: - /** - * Get a double, return 0 if not a double. - * - * @param ctx the context - * @param index the index - * @return the double - */ - static inline double get(Context &ctx, int index) - { - return duk_get_number(ctx, index); - } - - /** - * Check if value is a double. - * - * @param ctx the context - * @param index the index - * @return true if double - */ - static inline bool is(Context &ctx, int index) - { - return duk_is_number(ctx, index); - } - - /** - * Get a double, return defaultValue if the value is not a double. - * - * @param ctx the context - * @param index the index - * @param defaultValue the defaultValue - * @return the double or defaultValue - */ - static inline double optional(Context &ctx, int index, double defaultValue) - { - if (!duk_is_number(ctx, index)) { - return defaultValue; - } - - return duk_get_number(ctx, index); - } - - /** - * Push a double. - * - * @param ctx the context - * @param value the value - */ - static inline void push(Context &ctx, double value) - { - duk_push_number(ctx, value); - } - - /** - * Require a double, throws a JavaScript exception if not a double. - * - * @param ctx the context - * @param index the index - * @return the double - */ - static inline double require(Context &ctx, int index) - { - return duk_require_number(ctx, index); - } -}; - -/** - * @class TypeInfo<std::string> - * @brief Default implementation for std::string. - * - * Provides: get, is, optional, push, require. - * - * Note: the functions allows embedded '\0'. - */ -template <> -class TypeInfo<std::string> { -public: - /** - * Get a string, return 0 if not a string. - * - * @param ctx the context - * @param index the index - * @return the string - */ - static inline std::string get(Context &ctx, int index) - { - duk_size_t size; - const char *text = duk_get_lstring(ctx, index, &size); - - return std::string{text, size}; - } - - /** - * Check if value is a string. - * - * @param ctx the context - * @param index the index - * @return true if string - */ - static inline bool is(Context &ctx, int index) - { - return duk_is_string(ctx, index); - } - - /** - * Get a string, return defaultValue if the value is not an string. - * - * @param ctx the context - * @param index the index - * @param defaultValue the defaultValue - * @return the string or defaultValue - */ - static inline std::string optional(Context &ctx, int index, std::string defaultValue) - { - if (!duk_is_string(ctx, index)) { - return defaultValue; - } - - return get(ctx, index); - } - - /** - * Push a string. - * - * @param ctx the context - * @param value the value - */ - static inline void push(Context &ctx, const std::string &value) - { - duk_push_lstring(ctx, value.c_str(), value.length()); - } - - /** - * Require a string, throws a JavaScript exception if not a string. - * - * @param ctx the context - * @param index the index - * @return the string - */ - static inline std::string require(Context &ctx, int index) - { - duk_size_t size; - const char *text = duk_require_lstring(ctx, index, &size); - - return std::string{text, size}; - } -}; - -/** - * @class TypeInfo<const char *> - * @brief Default implementation for const char literals. - * - * Provides: get, is, optional, push, require. - */ -template <> -class TypeInfo<const char *> { -public: - /** - * Get a string, return 0 if not a string. - * - * @param ctx the context - * @param index the index - * @return the string - */ - static inline const char *get(Context &ctx, int index) - { - return duk_get_string(ctx, index); - } - - /** - * Check if value is a string. - * - * @param ctx the context - * @param index the index - * @return true if string - */ - static inline bool is(Context &ctx, int index) - { - return duk_is_string(ctx, index); - } - - /** - * 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 const char *optional(Context &ctx, int index, const char *defaultValue) - { - if (!duk_is_string(ctx, index)) { - return defaultValue; - } - - return duk_get_string(ctx, index); - } - - /** - * Push a string. - * - * @param ctx the context - * @param value the value - */ - static inline void push(Context &ctx, const char *value) - { - duk_push_string(ctx, value); - } - - /** - * Require a string, throws a JavaScript exception if not a string. - * - * @param ctx the context - * @param index the index - * @return the string - */ - static inline const char *require(Context &ctx, int index) - { - return duk_require_string(ctx, index); - } -}; - -/** - * @brief Implementation for non-managed pointers. - * - * Provides: get, is, optional, push, require. - */ -template <typename T> -class TypeInfo<RawPointer<T>> { -public: - /** - * Get a pointer, return nullptr if not a pointer. - * - * @param ctx the context - * @param index the index - * @return the pointer - */ - static inline T *get(Context &ctx, int index) - { - return static_cast<T *>(duk_to_pointer(ctx, index)); - } - - /** - * Check if value is a pointer. - * - * @param ctx the context - * @param index the index - * @return true if pointer - */ - static inline bool is(Context &ctx, int index) - { - return duk_is_pointer(ctx, index); - } - - /** - * Get a pointer, return defaultValue if the value is not a pointer. - * - * @param ctx the context - * @param index the index - * @param defaultValue the defaultValue - * @return the pointer or defaultValue - */ - static inline T *optional(Context &ctx, int index, RawPointer<T> defaultValue) - { - if (!duk_is_pointer(ctx, index)) { - return defaultValue.object; - } - - return static_cast<T *>(duk_to_pointer(ctx, index)); - } - - /** - * Push a pointer. - * - * @param ctx the context - * @param value the value - */ - static inline void push(Context &ctx, const RawPointer<T> &value) - { - duk_push_pointer(ctx, value.object); - } - - /** - * Require a pointer, throws a JavaScript exception if not a pointer. - * - * @param ctx the context - * @param index the index - * @return the pointer - */ - static inline T *require(Context &ctx, int index) - { - return static_cast<T *>(duk_require_pointer(ctx, index)); - } -}; - -/** - * @class TypeInfo<Function> - * @brief Push C++ function to the stack. - * - * Provides: push. - * - * This implementation push a Duktape/C function that is wrapped as C++ for convenience. - */ -template <> -class TypeInfo<Function> { -public: - /** - * Push the C++ function, it is wrapped as Duktape/C function and allocated on the heap by moving the - * std::function. - * - * @param ctx the context - * @param fn the function - */ - static void push(Context &ctx, Function fn); -}; - -/** - * @class TypeInfo<FunctionMap> - * @brief Put the functions to the object at the top of the stack. - * - * Provides: push. - */ -template <> -class TypeInfo<FunctionMap> { -public: - /** - * Push a map of function to the object at the top of the stack. - * - * @param ctx the context - * @param map the map of function - */ - static inline void push(Context &ctx, const FunctionMap &map) - { - for (const auto &entry : map) { - ctx.putProperty(-1, entry.first, entry.second); - } - } -}; - -/** - * @class TypeInfo<Object> - * @brief Push empty object to the stack. - * - * Provides: is, push. - */ -template <> -class TypeInfo<Object> { -public: - /** - * Check if value is an object. - * - * @param ctx the context - * @param index the index - * @return true if object - */ - static inline bool is(Context &ctx, int index) - { - return duk_is_object(ctx, index); - } - - /** - * Create an empty object on the stack. - * - * @param ctx the context - */ - static inline void push(Context &ctx, const Object &) - { - duk_push_object(ctx); - } -}; - -/** - * @class TypeInfo<Array> - * @brief Push empty array to the stack. - * - * Provides: is, push. - */ -template <> -class TypeInfo<Array> { -public: - /** - * Check if value is a array. - * - * @param ctx the context - * @param index the index - * @return true if array - */ - static inline bool is(Context &ctx, int index) - { - return duk_is_array(ctx, index); - } - - /** - * Create an empty array on the stack. - * - * @param ctx the context - */ - static inline void push(Context &ctx, const Array &) - { - duk_push_array(ctx); - } -}; - -/** - * @class TypeInfo<Undefined> - * @brief Push undefined value to the stack. - * - * Provides: is, push. - */ -template <> -class TypeInfo<Undefined> { -public: - /** - * Check if value is undefined. - * - * @param ctx the context - * @param index the index - * @return true if undefined - */ - static inline bool is(Context &ctx, int index) - { - return duk_is_undefined(ctx, index); - } - - /** - * Push undefined value on the stack. - * - * @param ctx the context - */ - static inline void push(Context &ctx, const Undefined &) - { - duk_push_undefined(ctx); - } -}; - -/** - * @class TypeInfo<Null> - * @brief Push null value to the stack. - * - * Provides: is, push. - */ -template <> -class TypeInfo<Null> { -public: - /** - * Check if value is null. - * - * @param ctx the context - * @param index the index - * @return true if null - */ - static inline bool is(Context &ctx, int index) - { - return duk_is_null(ctx, index); - } - - /** - * Push null value on the stack. - * - * @param ctx the context - */ - static inline void push(Context &ctx, const Null &) - { - duk_push_null(ctx); - } -}; - -/** - * @brief Push this binding into the stack. - * - * Provides: push. - */ -template <> -class TypeInfo<This> { -public: - /** - * Push this function into the stack. - * - * @param ctx the context - */ - static inline void push(Context &ctx, const This &) - { - duk_push_this(ctx); - } -}; - -/** - * @class TypeInfo<Global> - * @brief Push the global object to the stack. - * - * Provides: push. - */ -template <> -class TypeInfo<Global> { -public: - /** - * Push the global object into the stack. - * - * @param ctx the context - */ - static inline void push(Context &ctx, const Global &) - { - duk_push_global_object(ctx); - } -}; - -/** - * @brief Push a map of key-value pair as objects. - * - * Provides: push. - * - * This class is convenient for settings constants such as enums, string and such. - */ -template <typename T> -class TypeInfo<std::unordered_map<std::string, T>> { -public: - /** - * Put all values from the map as properties to the object at the top of the stack. - * - * @param ctx the context - * @param map the values - * @note You need an object at the top of the stack before calling this function - */ - static void push(Context &ctx, const std::unordered_map<std::string, T> &map) - { - for (const auto &pair : map) { - TypeInfo<T>::push(ctx, pair.second); - duk_put_prop_string(ctx, -2, pair.first.c_str()); - } - } -}; - -/** - * @brief Push or get vectors as JavaScript arrays. - * - * Provides: get, push. - */ -template <typename T> -class TypeInfo<std::vector<T>> { -public: - /** - * Get an array from the stack. - * - * @param ctx the context - * @param index the array index - * @return the array or empty array if the value is not an array - */ - static std::vector<T> get(Context &ctx, int index) - { - std::vector<T> result; - - if (!duk_is_array(ctx, -1)) { - return result; - } - - int total = duk_get_length(ctx, index); - for (int i = 0; i < total; ++i) { - result.push_back(ctx.getProperty<T>(index, i)); - } - - return result; - } - - /** - * Create an array with the specified values. - * - * @param ctx the context - * @param array the values - */ - static void push(Context &ctx, const std::vector<T> &array) - { - duk_push_array(ctx); - - int i = 0; - for (const auto &v : array) { - TypeInfo<T>::push(ctx, v); - duk_put_prop_index(ctx, -2, i++); - } - } -}; - -/** - * @brief Implementation of managed shared_ptr - * @see Shared - */ -template <typename T> -class TypeInfo<Shared<T>> { -private: - static void apply(Context &ctx, std::shared_ptr<T> value) - { - duk_push_boolean(ctx, false); - duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted"); - duk_push_pointer(ctx, new std::shared_ptr<T>(value)); - duk_put_prop_string(ctx, -2, "\xff""\xff""js-shared-ptr"); - duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t { - duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted"); - - if (duk_to_boolean(ctx, -1)) { - duk_push_boolean(ctx, true); - duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted"); - 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_pop(ctx); - - 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(Context &ctx, Shared<T> value) - { - 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(Context &ctx, Shared<T> value) - { - duk_push_object(ctx); - apply(ctx, value.object); - value.object->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(Context &ctx, int 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 TypeInfo<Pointer<T>> { -private: - static void apply(Context &ctx, T *value) - { - duk_push_boolean(ctx, false); - duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted"); - duk_push_pointer(ctx, value); - duk_put_prop_string(ctx, -2, "\xff""\xff""js-ptr"); - duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t { - duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted"); - - if (duk_to_boolean(ctx, -1)) { - duk_push_boolean(ctx, true); - duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted"); - duk_get_prop_string(ctx, 0, "\xff""\xff""js-ptr"); - delete static_cast<T *>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - } - - duk_pop(ctx); - - 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(Context &ctx, Pointer<T> value) - { - 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(Context &ctx, Pointer<T> value) - { - duk_push_object(ctx); - apply(ctx, value.object); - value.object->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(Context &ctx, int 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; - } -}; - -} // !js - -#endif // !_JS_H_ +/* + * Js.h -- JavaScript C++14 wrapper for Duktape + * + * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _JS_H_ +#define _JS_H_ + +/** + * @file Js.h + * @brief Bring JavaScript using Duktape + * + * This file provides usual Duktape function renamed and placed into `js` namespace. It also replaces error + * code with exceptions when possible. + * + * For convenience, this file also provides templated functions, overloads and much more. + */ + +#include <functional> +#include <memory> +#include <string> +#include <type_traits> +#include <unordered_map> +#include <utility> +#include <vector> + +#include <duktape.h> + +/** + * Duktape C++ namespace wrapper. + */ +namespace js { + +class Context; + +/** + * Typedef for readability. + */ +using ContextPtr = duk_context *; + +/* + * Basic types to manipulate with the stack + * ------------------------------------------------------------------ + * + * The following types can be used in some of the operations like Context::push or Context::is, they are defined + * usually as empty classes to determine the appropriate action to execute. + * + * For example, `ctx.push(js::Object{})` will push an empty object into the stack. + */ + +/** + * @class Object + * @brief Empty class tag for push() function. + */ +class Object { +}; + +/** + * @class Array + * @brief Empty class tag for push() function. + */ +class Array { +}; + +/** + * @class Global + * @brief Empty class tag to push the global object. + */ +class Global { +}; + +/** + * @class Undefined + * @brief Empty class tag to push undefined to the stack. + */ +class Undefined { +}; + +/** + * @class Null + * @brief Empty class tag to push null to the stack. + */ +class Null { +}; + +/** + * @class This + * @brief Empty class tag to push this binding to the stack. + */ +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; +}; + +/* + * Extended type manipulation + * ------------------------------------------------------------------ + * + * The following types are different as there are no equivalent in the native Duktape API, they are available for + * convenience. + */ + +/** + * @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. + * + * The only requirement is to have the function `void prototype(Context &ctx)` in your class T. + */ +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}; +}; + +/** + * @class Function + * @brief Duktape/C function definition. + * + * This class wraps the std::function as a Duktape/C function by storing a copied pointer. + */ +class Function { +public: + /** + * The function pointer, must not be null. + */ + std::function<int (Context &)> function; + + /** + * Number of args that the function takes + */ + int nargs{0}; +}; + +/** + * Map of functions to set on an object. + */ +using FunctionMap = std::unordered_map<std::string, Function>; + +/** + * Map of string to type, ideal for setting constants like enums. + */ +template <typename Type> +using Map = std::unordered_map<std::string, Type>; + +/** + * @class ErrorInfo + * @brief Error description. + * + * This class fills the fields got in an Error object. + */ +class ErrorInfo : public std::exception { +public: + std::string name; //!< name of error + std::string message; //!< error message + std::string stack; //!< stack if available + std::string fileName; //!< filename if applicable + int lineNumber{0}; //!< line number if applicable + + /** + * Get the error message. This effectively returns message field. + * + * @return the message + */ + const char *what() const noexcept override + { + return message.c_str(); + } +}; + +/** + * @class TypeInfo + * @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 TypeInfo { +}; + +/** + * @class File + * @brief Evaluate script from file. + * @see Context::eval + * @see Context::peval + */ +class File { +public: + /** + * Path to the file. + */ + std::string path; + + /** + * Evaluate the file. + * + * @param ctx the context + */ + inline void eval(duk_context *ctx) + { + duk_eval_file(ctx, path.c_str()); + } + + /** + * Evaluate in protected mode the file. + * + * @param ctx the context + */ + inline int peval(duk_context *ctx) + { + return duk_peval_file(ctx, path.c_str()); + } +}; + +/** + * @class Script + * @brief Evaluate script from raw text. + * @see Context::eval + * @see Context::peval + */ +class Script { +public: + /** + * The script content. + */ + std::string text; + + /** + * Evaluate the script. + * + * @param ctx the context + */ + inline void eval(duk_context *ctx) + { + duk_eval_string(ctx, text.c_str()); + } + + /** + * Evaluate in protected mode the script. + * + * @param ctx the context + */ + inline int peval(duk_context *ctx) + { + return duk_peval_string(ctx, text.c_str()); + } +}; + +/** + * @class Context + * @brief RAII based Duktape handler. + * + * This class is implicitly convertible to duk_context for convenience. + */ +class Context { +private: + using Deleter = void (*)(duk_context *); + using Handle = std::unique_ptr<duk_context, Deleter>; + + Handle m_handle; + + /* Move and copy forbidden */ + Context(const Context &) = delete; + Context &operator=(const Context &) = delete; + Context(const Context &&) = delete; + Context &operator=(const Context &&) = delete; + +public: + /** + * Create default context. + */ + inline Context() + : m_handle{duk_create_heap_default(), duk_destroy_heap} + { + } + + /** + * Create borrowed context that will not be deleted. + * + * @param ctx the pointer to duk_context + */ + inline Context(ContextPtr ctx) noexcept + : m_handle{ctx, [] (ContextPtr) {}} + { + } + + /** + * Convert the context to the native Duktape/C type. + * + * @return the duk_context + */ + inline operator duk_context *() noexcept + { + return m_handle.get(); + } + + /** + * Convert the context to the native Duktape/C type. + * + * @return the duk_context + */ + inline operator duk_context *() const noexcept + { + return m_handle.get(); + } + + /* + * Basic functions + * ---------------------------------------------------------- + * + * The following functions are just standard wrappers around the native Duktape C functions, they are + * defined with the same signature except that for convenience some parameters have default sane values. + */ + + /** + * Call the object at the top of the stack. + * + * @param ctx the context + * @param nargs the number of arguments + * @note Non-protected + */ + inline void call(unsigned nargs = 0) + { + duk_call(m_handle.get(), nargs); + } + + /** + * Copy a value from from to to, overwriting the previous value. If either index is invalid, throws an error. + * + * @param from the from index + * @param to the destination + */ + inline void copy(int from, int to) + { + duk_copy(m_handle.get(), from, to); + } + + /** + * Define a property. + * + * @param index the object index + * @param flags the flags + * @note Wrapper of duk_def_prop + */ + inline void defineProperty(int index, int flags) + { + duk_def_prop(m_handle.get(), index, flags); + } + + /** + * Delete a property. + * + * @param index the object index + * @return true if deleted + * @note Wrapper of duk_del_prop + */ + inline bool deleteProperty(int index) + { + return duk_del_prop(m_handle.get(), index); + } + + /** + * Delete a property by index. + * + * @param index the object index + * @param position the property index + * @return true if deleted + * @note Wrapper of duk_del_prop_index + */ + inline bool deleteProperty(int index, int position) + { + return duk_del_prop_index(m_handle.get(), index, position); + } + + /** + * Delete a property by name. + * + * @param index the object index + * @param name the property name + * @return true if deleted + * @note Wrapper of duk_del_prop_string + */ + inline bool deleteProperty(int index, const std::string &name) + { + return duk_del_prop_string(m_handle.get(), index, name.c_str()); + } + + /** + * Push a duplicate of value at from_index to the stack. If from_index is invalid, throws an error. + * + * @param index the value to copy + * @note Wrapper of duk_dup + */ + inline void dup(int index = -1) + { + duk_dup(m_handle.get(), index); + } + + /** + * Evaluate a non-protected chunk that is at the top of the stack. + */ + inline void eval() + { + duk_eval(m_handle.get()); + } + + /** + * Check if the object as a property. + * + * @param index the object index + * @return true if has + * @note Wrapper of duk_has_prop + */ + inline bool hasProperty(int index) + { + return duk_has_prop(m_handle.get(), index); + } + + /** + * Check if the object as a property by index. + * + * @param index the object index + * @param position the property index + * @return true if has + * @note Wrapper of duk_has_prop_index + */ + inline bool hasProperty(int index, int position) + { + return duk_has_prop_index(m_handle.get(), index, position); + } + + /** + * Check if the object as a property by string + * + * @param index the object index + * @param name the property name + * @return true if has + * @note Wrapper of duk_has_prop_string + */ + inline bool hasProperty(int index, const std::string &name) + { + return duk_has_prop_string(m_handle.get(), index, name.c_str()); + } + + /** + * Check if idx1 is an instance of idx2. + * + * @param ctx the context + * @param idx1 the value to test + * @param idx2 the instance requested + * @return true if idx1 is instance of idx2 + * @note Wrapper of duk_instanceof + */ + inline bool instanceof(int idx1, int idx2) + { + return duk_instanceof(m_handle.get(), idx1, idx2); + } + + /** + * Insert a value at to with a value popped from the stack top. The previous value at to and any values above + * it are moved up the stack by a step. If to is an invalid index, throws an error. + * + * @note Negative indices are evaluated prior to popping the value at the stack top + * @param to the destination + * @note Wrapper of duk_insert + */ + inline void insert(int to) + { + duk_insert(m_handle.get(), to); + } + + /** + * Pop a certain number of values from the top of the stack. + * + * @param ctx the context + * @param count the number of values to pop + * @note Wrapper of duk_pop_n + */ + inline void pop(unsigned count = 1) + { + duk_pop_n(m_handle.get(), count); + } + + /** + * Remove value at index. Elements above index are shifted down the stack by a step. If to is an invalid index, + * throws an error. + * + * @param index the value to remove + * @note Wrapper of duk_remove + */ + inline void remove(int index) + { + duk_remove(m_handle.get(), index); + } + + /** + * Replace value at to_index with a value popped from the stack top. If to_index is an invalid index, + * throws an error. + * + * @param index the value to replace by the value at the top of the stack + * @note Negative indices are evaluated prior to popping the value at the stack top. + * @note Wrapper of duk_replace + */ + inline void replace(int index) + { + duk_replace(m_handle.get(), index); + } + + /** + * Swap values at indices index1 and index2. If the indices are the same, the call is a no-op. If either index + * is invalid, throws an error. + * + * @param index1 the first index + * @param index2 the second index + * @note Wrapper of duk_swap + */ + inline void swap(int index1, int index2) + { + duk_swap(m_handle.get(), index1, index2); + } + + /** + * Get the current stack size. + * + * @param ctx the context + * @return the stack size + * @note Wrapper of duk_get_top + */ + inline int top() noexcept + { + return duk_get_top(m_handle.get()); + } + + /** + * Get the type of the value at the specified index. + * + * @param ctx the context + * @param index the idnex + * @return the type + * @note Wrapper of duk_get_type + */ + inline int type(int index) noexcept + { + return duk_get_type(m_handle.get(), index); + } + + /* + * Extended native functions + * ---------------------------------------------------------- + * + * The following functions have different behaviour than the original native Duktape C functions, see their + * descriptions for more information + */ + + /** + * Call in protected mode the object at the top of the stack. + * + * @param nargs the number of arguments + * @throw ErrorInfo on errors + * @note Wrapper of duk_pcall + */ + void pcall(unsigned nargs = 0); + + /** + * Evaluate a non-protected source. + * + * @param source the source + * @see File + * @see Script + * @note Wrapper of duk_eval + */ + template <typename Source> + inline void eval(Source &&source) + { + source.eval(m_handle.get()); + } + + /** + * Evaluate a protected chunk that is at the top of the stack. + * + * @throw ErrorInfo the error + * @note Wrapper of duk_peval + */ + void peval(); + + /** + * Evaluate a protected source. + * + * @param source the source + * @see File + * @see Script + * @throw ErrorInfo on failure + * @note Wrapper of duk_peval + */ + template <typename Source> + inline void peval(Source &&source) + { + if (source.peval(m_handle.get()) != 0) { + ErrorInfo info = error(-1); + duk_pop(m_handle.get()); + + throw info; + } + } + + /* + * Push / Get / Require / Is / Optional + * ---------------------------------------------------------- + * + * The following functions are used to push, get or check values from the stack. They use specialization + * of TypeInfo class. + */ + + /** + * Push a value into the stack. Calls TypeInfo<T>::push(*this, value); + * + * @param value the value to forward + */ + template <typename Type> + inline void push(Type &&value) + { + TypeInfo<std::decay_t<Type>>::push(*this, std::forward<Type>(value)); + } + + /** + * Generic template function to get a value from the stack. + * + * @param index the index + * @return the value + */ + template <typename Type> + inline auto get(int index) -> decltype(TypeInfo<Type>::get(*this, 0)) + { + return TypeInfo<Type>::get(*this, index); + } + + /** + * Require a type at the specified index. + * + * @param index the index + * @return the value + */ + template <typename Type> + inline auto require(int index) -> decltype(TypeInfo<Type>::require(*this, 0)) + { + return TypeInfo<Type>::require(*this, index); + } + + /** + * Check if a value is a type of T. + * + * The TypeInfo<T> must have `static bool is(ContextPtr ptr, int index)`. + * + * @param index the value index + * @return true if is the type + */ + template <typename T> + inline bool is(int index) + { + return TypeInfo<T>::is(*this, index); + } + + /** + * Get an optional value from the stack, if the value is not available of not the correct type, + * return defaultValue instead. + * + * The TypeInfo<T> must have `static T optional(Context &, int index, T &&defaultValue)`. + * + * @param index the value index + * @param defaultValue the value replacement + * @return the value or defaultValue + */ + template <typename Type> + inline auto optional(int index, Type &&defaultValue) + { + return TypeInfo<std::decay_t<Type>>::optional(*this, index, std::forward<Type>(defaultValue)); + } + + /* + * Properties management + * ---------------------------------------------------------- + * + * The following functions are used to read or set properties on objects or globals also using TypeInfo. + */ + + /** + * Get the property `name' as value from the object at the specified index. + * + * @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(int index, const std::string &name) -> decltype(get<Type>(0)) + { + duk_get_prop_string(m_handle.get(), index, name.c_str()); + decltype(get<Type>(0)) value = get<Type>(-1); + duk_pop(m_handle.get()); + + return value; + } + + /** + * Get a property by index, for arrays. + * + * @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(int index, int position) -> decltype(get<Type>(0)) + { + duk_get_prop_index(m_handle.get(), index, position); + decltype(get<Type>(0)) value = get<Type>(-1); + duk_pop(m_handle.get()); + + return value; + } + + /** + * Get the property `name' and push it to the stack from the object at the specified index. + * + * @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(int index, const std::string &name) + { + duk_get_prop_string(m_handle.get(), index, name.c_str()); + } + + /** + * Get the property by index and push it to the stack from the object at the specified index. + * + * @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(int index, int position) + { + duk_get_prop_index(m_handle.get(), index, position); + } + + /** + * Set a property to the object at the specified index. + * + * @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(int index, const std::string &name, Type &&value) + { + index = duk_normalize_index(m_handle.get(), index); + + push(std::forward<Type>(value)); + duk_put_prop_string(m_handle.get(), index, name.c_str()); + } + + /** + * Set a property by index, for arrays. + * + * @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(int index, int position, Type &&value) + { + index = duk_normalize_index(m_handle.get(), index); + + push(std::forward<Type>(value)); + duk_put_prop_index(m_handle.get(), index, position); + } + + /** + * 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(int index, const std::string &name) + { + duk_put_prop_string(m_handle.get(), 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(int index, int position) + { + duk_put_prop_index(m_handle.get(), index, position); + } + + /** + * Get a global value. + * + * @param name the name of the global variable + * @return the value + */ + template <typename Type> + inline auto getGlobal(const std::string &name, std::enable_if_t<!std::is_void<Type>::value> * = nullptr) -> decltype(get<Type>(0)) + { + duk_get_global_string(m_handle.get(), name.c_str()); + decltype(get<Type>(0)) value = get<Type>(-1); + duk_pop(m_handle.get()); + + return value; + } + + /** + * Overload that push the value at the top of the stack instead of returning it. + */ + template <typename Type> + inline void getGlobal(const std::string &name, std::enable_if_t<std::is_void<Type>::value> * = nullptr) noexcept + { + duk_get_global_string(m_handle.get(), name.c_str()); + } + + /** + * Set a global variable. + * + * @param name the name of the global variable + * @param type the value to set + */ + template <typename Type> + inline void putGlobal(const std::string &name, Type&& type) + { + push(std::forward<Type>(type)); + duk_put_global_string(m_handle.get(), name.c_str()); + } + + /** + * Put the value at the top of the stack as global property. + * + * @param name the property name + */ + inline void putGlobal(const std::string &name) + { + duk_put_global_string(m_handle.get(), name.c_str()); + } + + /* + * Extra functions + * ---------------------------------------------------------- + * + * The following functions are implemented for convenience and do not exists in the native Duktape API. + */ + + /** + * Get the error object when a JavaScript error has been thrown (e.g. eval failure). + * + * @param index the index + * @return the information + */ + ErrorInfo error(int index); + + /** + * Enumerate an object or an array at the specified index. + * + * @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(int index, duk_uint_t flags, duk_bool_t getvalue, Func&& func) + { + duk_enum(m_handle.get(), index, flags); + + while (duk_next(m_handle.get(), -1, getvalue)) { + func(*this); + duk_pop_n(m_handle.get(), 1 + (getvalue ? 1 : 0)); + } + + duk_pop(m_handle.get()); + } + + /** + * Return the this binding of the current function. + * + * @return the this binding as the template given + */ + template <typename T> + inline auto self() -> decltype(TypeInfo<T>::get(*this, 0)) + { + duk_push_this(m_handle.get()); + decltype(TypeInfo<T>::get(*this, 0)) value = TypeInfo<T>::get(*this, -1); + duk_pop(m_handle.get()); + + return value; + } + + /** + * Throw an ECMAScript exception. + * + * @param ex the exception + */ + template <typename Exception> + void raise(const Exception &ex) + { + ex.create(m_handle.get()); + + duk_push_string(m_handle.get(), ex.name().c_str()); + duk_put_prop_string(m_handle.get(), -2, "name"); + duk_throw(m_handle.get()); + } + + /** + * Construct the object in place, setting value as this binding. + * + * The TypeInfo<T> must have the following requirements: + * + * - static void construct(Context &, T): must update this with the value and keep the stack unchanged + * + * @param value the value to forward + * @see self + */ + template <typename T> + inline void construct(T &&value) + { + TypeInfo<std::decay_t<T>>::construct(*this, std::forward<T>(value)); + } +}; + +/* ------------------------------------------------------------------ + * Exception handling + * ------------------------------------------------------------------ */ + +/** + * @class Error + * @brief Base ECMAScript error class. + * @warning Override the function create for your own exceptions + */ +class Error { +protected: + std::string m_name; //!< Name of exception (e.g RangeError) + std::string m_message; //!< The message + + /** + * Constructor with a type of error specified, specially designed for derived errors. + * + * @param name the error name (e.g RangeError) + * @param message the message + */ + inline Error(std::string name, std::string message) noexcept + : m_name{std::move(name)} + , m_message{std::move(message)} + { + } + +public: + /** + * Constructor with a message. + * + * @param message the message + */ + inline Error(std::string message) noexcept + : m_name{"Error"} + , m_message{std::move(message)} + { + } + + /** + * Get the error type (e.g RangeError). + * + * @return the name + */ + inline const std::string &name() const noexcept + { + return m_name; + } + + /** + * Create the exception on the stack. + * + * @note the default implementation search for the global variables + * @param ctx the context + */ + virtual void create(ContextPtr ctx) const noexcept + { + duk_get_global_string(ctx, m_name.c_str()); + duk_push_string(ctx, m_message.c_str()); + duk_new(ctx, 1); + } +}; + +/** + * @class EvalError + * @brief Error in eval() function. + */ +class EvalError : public Error { +public: + /** + * Construct an EvalError. + * + * @param message the message + */ + inline EvalError(std::string message) noexcept + : Error{"EvalError", std::move(message)} + { + } +}; + +/** + * @class RangeError + * @brief Value is out of range. + */ +class RangeError : public Error { +public: + /** + * Construct an RangeError. + * + * @param message the message + */ + inline RangeError(std::string message) noexcept + : Error{"RangeError", std::move(message)} + { + } +}; + +/** + * @class ReferenceError + * @brief Trying to use a variable that does not exist. + */ +class ReferenceError : public Error { +public: + /** + * Construct an ReferenceError. + * + * @param message the message + */ + inline ReferenceError(std::string message) noexcept + : Error{"ReferenceError", std::move(message)} + { + } +}; + +/** + * @class SyntaxError + * @brief Syntax error in the script. + */ +class SyntaxError : public Error { +public: + /** + * Construct an SyntaxError. + * + * @param message the message + */ + inline SyntaxError(std::string message) noexcept + : Error{"SyntaxError", std::move(message)} + { + } +}; + +/** + * @class TypeError + * @brief Invalid type given. + */ +class TypeError : public Error { +public: + /** + * Construct an TypeError. + * + * @param message the message + */ + inline TypeError(std::string message) noexcept + : Error{"TypeError", std::move(message)} + { + } +}; + +/** + * @class URIError + * @brief URI manipulation failure. + */ +class URIError : public Error { +public: + /** + * Construct an URIError. + * + * @param message the message + */ + inline URIError(std::string message) noexcept + : Error{"URIError", std::move(message)} + { + } +}; + +/* ------------------------------------------------------------------ + * Standard overloads for TypeInfo<T> + * ------------------------------------------------------------------ */ + +/** + * @class TypeInfo<int> + * @brief Default implementation for int. + * + * Provides: get, is, optional, push, require. + */ +template <> +class TypeInfo<int> { +public: + /** + * Get an integer, return 0 if not an integer. + * + * @param ctx the context + * @param index the index + * @return the integer + */ + static inline int get(Context &ctx, int index) + { + return duk_get_int(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); + } + + /** + * 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 int optional(Context &ctx, int index, int defaultValue) + { + if (!duk_is_number(ctx, index)) { + return defaultValue; + } + + return duk_get_int(ctx, index); + } + + /** + * Push an integer. + * + * @param ctx the context + * @param value the value + */ + static inline void push(Context &ctx, int value) + { + duk_push_int(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 int require(Context &ctx, int index) + { + return duk_require_int(ctx, index); + } +}; + +/** + * @class TypeInfo<bool> + * @brief Default implementation for bool. + * + * Provides: get, is, optional, push, require. + */ +template <> +class TypeInfo<bool> { +public: + /** + * Get a boolean, return 0 if not a boolean. + * + * @param ctx the context + * @param index the index + * @return the boolean + */ + static inline bool get(Context &ctx, int index) + { + return duk_get_boolean(ctx, index); + } + + /** + * Check if value is a boolean. + * + * @param ctx the context + * @param index the index + * @return true if boolean + */ + static inline bool is(Context &ctx, int index) + { + return duk_is_boolean(ctx, index); + } + + /** + * Get a bool, return defaultValue if the value is not a boolean. + * + * @param ctx the context + * @param index the index + * @param defaultValue the defaultValue + * @return the boolean or defaultValue + */ + static inline bool optional(Context &ctx, int index, bool defaultValue) + { + if (!duk_is_boolean(ctx, index)) { + return defaultValue; + } + + return duk_get_boolean(ctx, index); + } + + /** + * Push a boolean. + * + * @param ctx the context + * @param value the value + */ + static inline void push(Context &ctx, bool value) + { + duk_push_boolean(ctx, value); + } + + /** + * Require a boolean, throws a JavaScript exception if not a boolean. + * + * @param ctx the context + * @param index the index + * @return the boolean + */ + static inline bool require(Context &ctx, int index) + { + return duk_require_boolean(ctx, index); + } +}; + +/** + * @class TypeInfo<double> + * @brief Default implementation for double. + * + * Provides: get, is, optional, push, require. + */ +template <> +class TypeInfo<double> { +public: + /** + * Get a double, return 0 if not a double. + * + * @param ctx the context + * @param index the index + * @return the double + */ + static inline double get(Context &ctx, int index) + { + return duk_get_number(ctx, index); + } + + /** + * Check if value is a double. + * + * @param ctx the context + * @param index the index + * @return true if double + */ + static inline bool is(Context &ctx, int index) + { + return duk_is_number(ctx, index); + } + + /** + * Get a double, return defaultValue if the value is not a double. + * + * @param ctx the context + * @param index the index + * @param defaultValue the defaultValue + * @return the double or defaultValue + */ + static inline double optional(Context &ctx, int index, double defaultValue) + { + if (!duk_is_number(ctx, index)) { + return defaultValue; + } + + return duk_get_number(ctx, index); + } + + /** + * Push a double. + * + * @param ctx the context + * @param value the value + */ + static inline void push(Context &ctx, double value) + { + duk_push_number(ctx, value); + } + + /** + * Require a double, throws a JavaScript exception if not a double. + * + * @param ctx the context + * @param index the index + * @return the double + */ + static inline double require(Context &ctx, int index) + { + return duk_require_number(ctx, index); + } +}; + +/** + * @class TypeInfo<std::string> + * @brief Default implementation for std::string. + * + * Provides: get, is, optional, push, require. + * + * Note: the functions allows embedded '\0'. + */ +template <> +class TypeInfo<std::string> { +public: + /** + * Get a string, return 0 if not a string. + * + * @param ctx the context + * @param index the index + * @return the string + */ + static inline std::string get(Context &ctx, int index) + { + duk_size_t size; + const char *text = duk_get_lstring(ctx, index, &size); + + return std::string{text, size}; + } + + /** + * Check if value is a string. + * + * @param ctx the context + * @param index the index + * @return true if string + */ + static inline bool is(Context &ctx, int index) + { + return duk_is_string(ctx, index); + } + + /** + * Get a string, return defaultValue if the value is not an string. + * + * @param ctx the context + * @param index the index + * @param defaultValue the defaultValue + * @return the string or defaultValue + */ + static inline std::string optional(Context &ctx, int index, std::string defaultValue) + { + if (!duk_is_string(ctx, index)) { + return defaultValue; + } + + return get(ctx, index); + } + + /** + * Push a string. + * + * @param ctx the context + * @param value the value + */ + static inline void push(Context &ctx, const std::string &value) + { + duk_push_lstring(ctx, value.c_str(), value.length()); + } + + /** + * Require a string, throws a JavaScript exception if not a string. + * + * @param ctx the context + * @param index the index + * @return the string + */ + static inline std::string require(Context &ctx, int index) + { + duk_size_t size; + const char *text = duk_require_lstring(ctx, index, &size); + + return std::string{text, size}; + } +}; + +/** + * @class TypeInfo<const char *> + * @brief Default implementation for const char literals. + * + * Provides: get, is, optional, push, require. + */ +template <> +class TypeInfo<const char *> { +public: + /** + * Get a string, return 0 if not a string. + * + * @param ctx the context + * @param index the index + * @return the string + */ + static inline const char *get(Context &ctx, int index) + { + return duk_get_string(ctx, index); + } + + /** + * Check if value is a string. + * + * @param ctx the context + * @param index the index + * @return true if string + */ + static inline bool is(Context &ctx, int index) + { + return duk_is_string(ctx, index); + } + + /** + * 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 const char *optional(Context &ctx, int index, const char *defaultValue) + { + if (!duk_is_string(ctx, index)) { + return defaultValue; + } + + return duk_get_string(ctx, index); + } + + /** + * Push a string. + * + * @param ctx the context + * @param value the value + */ + static inline void push(Context &ctx, const char *value) + { + duk_push_string(ctx, value); + } + + /** + * Require a string, throws a JavaScript exception if not a string. + * + * @param ctx the context + * @param index the index + * @return the string + */ + static inline const char *require(Context &ctx, int index) + { + return duk_require_string(ctx, index); + } +}; + +/** + * @brief Implementation for non-managed pointers. + * + * Provides: get, is, optional, push, require. + */ +template <typename T> +class TypeInfo<RawPointer<T>> { +public: + /** + * Get a pointer, return nullptr if not a pointer. + * + * @param ctx the context + * @param index the index + * @return the pointer + */ + static inline T *get(Context &ctx, int index) + { + return static_cast<T *>(duk_to_pointer(ctx, index)); + } + + /** + * Check if value is a pointer. + * + * @param ctx the context + * @param index the index + * @return true if pointer + */ + static inline bool is(Context &ctx, int index) + { + return duk_is_pointer(ctx, index); + } + + /** + * Get a pointer, return defaultValue if the value is not a pointer. + * + * @param ctx the context + * @param index the index + * @param defaultValue the defaultValue + * @return the pointer or defaultValue + */ + static inline T *optional(Context &ctx, int index, RawPointer<T> defaultValue) + { + if (!duk_is_pointer(ctx, index)) { + return defaultValue.object; + } + + return static_cast<T *>(duk_to_pointer(ctx, index)); + } + + /** + * Push a pointer. + * + * @param ctx the context + * @param value the value + */ + static inline void push(Context &ctx, const RawPointer<T> &value) + { + duk_push_pointer(ctx, value.object); + } + + /** + * Require a pointer, throws a JavaScript exception if not a pointer. + * + * @param ctx the context + * @param index the index + * @return the pointer + */ + static inline T *require(Context &ctx, int index) + { + return static_cast<T *>(duk_require_pointer(ctx, index)); + } +}; + +/** + * @class TypeInfo<Function> + * @brief Push C++ function to the stack. + * + * Provides: push. + * + * This implementation push a Duktape/C function that is wrapped as C++ for convenience. + */ +template <> +class TypeInfo<Function> { +public: + /** + * Push the C++ function, it is wrapped as Duktape/C function and allocated on the heap by moving the + * std::function. + * + * @param ctx the context + * @param fn the function + */ + static void push(Context &ctx, Function fn); +}; + +/** + * @class TypeInfo<FunctionMap> + * @brief Put the functions to the object at the top of the stack. + * + * Provides: push. + */ +template <> +class TypeInfo<FunctionMap> { +public: + /** + * Push a map of function to the object at the top of the stack. + * + * @param ctx the context + * @param map the map of function + */ + static inline void push(Context &ctx, const FunctionMap &map) + { + for (const auto &entry : map) { + ctx.putProperty(-1, entry.first, entry.second); + } + } +}; + +/** + * @class TypeInfo<Object> + * @brief Push empty object to the stack. + * + * Provides: is, push. + */ +template <> +class TypeInfo<Object> { +public: + /** + * Check if value is an object. + * + * @param ctx the context + * @param index the index + * @return true if object + */ + static inline bool is(Context &ctx, int index) + { + return duk_is_object(ctx, index); + } + + /** + * Create an empty object on the stack. + * + * @param ctx the context + */ + static inline void push(Context &ctx, const Object &) + { + duk_push_object(ctx); + } +}; + +/** + * @class TypeInfo<Array> + * @brief Push empty array to the stack. + * + * Provides: is, push. + */ +template <> +class TypeInfo<Array> { +public: + /** + * Check if value is a array. + * + * @param ctx the context + * @param index the index + * @return true if array + */ + static inline bool is(Context &ctx, int index) + { + return duk_is_array(ctx, index); + } + + /** + * Create an empty array on the stack. + * + * @param ctx the context + */ + static inline void push(Context &ctx, const Array &) + { + duk_push_array(ctx); + } +}; + +/** + * @class TypeInfo<Undefined> + * @brief Push undefined value to the stack. + * + * Provides: is, push. + */ +template <> +class TypeInfo<Undefined> { +public: + /** + * Check if value is undefined. + * + * @param ctx the context + * @param index the index + * @return true if undefined + */ + static inline bool is(Context &ctx, int index) + { + return duk_is_undefined(ctx, index); + } + + /** + * Push undefined value on the stack. + * + * @param ctx the context + */ + static inline void push(Context &ctx, const Undefined &) + { + duk_push_undefined(ctx); + } +}; + +/** + * @class TypeInfo<Null> + * @brief Push null value to the stack. + * + * Provides: is, push. + */ +template <> +class TypeInfo<Null> { +public: + /** + * Check if value is null. + * + * @param ctx the context + * @param index the index + * @return true if null + */ + static inline bool is(Context &ctx, int index) + { + return duk_is_null(ctx, index); + } + + /** + * Push null value on the stack. + * + * @param ctx the context + */ + static inline void push(Context &ctx, const Null &) + { + duk_push_null(ctx); + } +}; + +/** + * @brief Push this binding into the stack. + * + * Provides: push. + */ +template <> +class TypeInfo<This> { +public: + /** + * Push this function into the stack. + * + * @param ctx the context + */ + static inline void push(Context &ctx, const This &) + { + duk_push_this(ctx); + } +}; + +/** + * @class TypeInfo<Global> + * @brief Push the global object to the stack. + * + * Provides: push. + */ +template <> +class TypeInfo<Global> { +public: + /** + * Push the global object into the stack. + * + * @param ctx the context + */ + static inline void push(Context &ctx, const Global &) + { + duk_push_global_object(ctx); + } +}; + +/** + * @brief Push a map of key-value pair as objects. + * + * Provides: push. + * + * This class is convenient for settings constants such as enums, string and such. + */ +template <typename T> +class TypeInfo<std::unordered_map<std::string, T>> { +public: + /** + * Put all values from the map as properties to the object at the top of the stack. + * + * @param ctx the context + * @param map the values + * @note You need an object at the top of the stack before calling this function + */ + static void push(Context &ctx, const std::unordered_map<std::string, T> &map) + { + for (const auto &pair : map) { + TypeInfo<T>::push(ctx, pair.second); + duk_put_prop_string(ctx, -2, pair.first.c_str()); + } + } +}; + +/** + * @brief Push or get vectors as JavaScript arrays. + * + * Provides: get, push. + */ +template <typename T> +class TypeInfo<std::vector<T>> { +public: + /** + * Get an array from the stack. + * + * @param ctx the context + * @param index the array index + * @return the array or empty array if the value is not an array + */ + static std::vector<T> get(Context &ctx, int index) + { + std::vector<T> result; + + if (!duk_is_array(ctx, -1)) { + return result; + } + + int total = duk_get_length(ctx, index); + for (int i = 0; i < total; ++i) { + result.push_back(ctx.getProperty<T>(index, i)); + } + + return result; + } + + /** + * Create an array with the specified values. + * + * @param ctx the context + * @param array the values + */ + static void push(Context &ctx, const std::vector<T> &array) + { + duk_push_array(ctx); + + int i = 0; + for (const auto &v : array) { + TypeInfo<T>::push(ctx, v); + duk_put_prop_index(ctx, -2, i++); + } + } +}; + +/** + * @brief Implementation of managed shared_ptr + * @see Shared + */ +template <typename T> +class TypeInfo<Shared<T>> { +private: + static void apply(Context &ctx, std::shared_ptr<T> value) + { + duk_push_boolean(ctx, false); + duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted"); + duk_push_pointer(ctx, new std::shared_ptr<T>(value)); + duk_put_prop_string(ctx, -2, "\xff""\xff""js-shared-ptr"); + duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t { + duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted"); + + if (!duk_to_boolean(ctx, -1)) { + duk_push_boolean(ctx, true); + duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted"); + 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_pop(ctx); + + 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(Context &ctx, Shared<T> value) + { + 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(Context &ctx, Shared<T> value) + { + duk_push_object(ctx); + apply(ctx, value.object); + value.object->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(Context &ctx, int 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 TypeInfo<Pointer<T>> { +private: + static void apply(Context &ctx, T *value) + { + duk_push_boolean(ctx, false); + duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted"); + duk_push_pointer(ctx, value); + duk_put_prop_string(ctx, -2, "\xff""\xff""js-ptr"); + duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t { + duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted"); + + if (!duk_to_boolean(ctx, -1)) { + duk_push_boolean(ctx, true); + duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted"); + duk_get_prop_string(ctx, 0, "\xff""\xff""js-ptr"); + delete static_cast<T *>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + } + + duk_pop(ctx); + + 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(Context &ctx, Pointer<T> value) + { + 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(Context &ctx, Pointer<T> value) + { + duk_push_object(ctx); + apply(ctx, value.object); + value.object->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(Context &ctx, int 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; + } +}; + +} // !js + +#endif // !_JS_H_
--- a/C++/tests/Ini/main.cpp Wed Oct 21 21:24:04 2015 +0200 +++ b/C++/tests/Ini/main.cpp Wed Oct 21 21:24:13 2015 +0200 @@ -281,6 +281,16 @@ } } +TEST(Errors, notFound) +{ + try { + ini::Document doc{ini::File{"does not exists"}}; + + FAIL() << "Failure expected, got success"; + } catch (const std::exception &) { + } +} + int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv);
--- a/C++/tests/Js/main.cpp Wed Oct 21 21:24:04 2015 +0200 +++ b/C++/tests/Js/main.cpp Wed Oct 21 21:24:13 2015 +0200 @@ -647,6 +647,19 @@ class Dog { public: + bool &m_deleted; + bool m_updated{false}; + + Dog(bool &deleted) + : m_deleted{deleted} + { + } + + ~Dog() + { + m_deleted = true; + } + void prototype(Context &ctx) { /* Create a temporary for the test */ @@ -659,27 +672,34 @@ return 0; }}); } - - bool m_updated{false}; }; TEST(Shared, simple) { - Context ctx; - std::shared_ptr<Dog> p{new Dog}; + bool deleted{false}; + { + std::shared_ptr<Dog> p{new Dog(deleted)}; + + { + Context ctx; - ctx.push(Shared<Dog>{p}); - std::shared_ptr<Dog> p2 = ctx.get<Shared<Dog>>(-1); + ctx.push(Shared<Dog>{p}); + std::shared_ptr<Dog> p2 = ctx.get<Shared<Dog>>(-1); + + p2->m_updated = true; - p2->m_updated = true; + ASSERT_TRUE(p->m_updated); + } + } - ASSERT_TRUE(p->m_updated); + ASSERT_TRUE(deleted); } TEST(Shared, self) { + bool deleted{false}; Context ctx; - std::shared_ptr<Dog> p{new Dog}; + std::shared_ptr<Dog> p{new Dog(deleted)}; try { ctx.putGlobal("player", Shared<Dog>{p}); @@ -697,6 +717,19 @@ class Cat { public: + bool &m_deleted; + bool m_updated{false}; + + Cat(bool &deleted) + : m_deleted{deleted} + { + } + + ~Cat() + { + m_deleted = true; + } + void prototype(Context &ctx) { /* Create a temporary for the test */ @@ -709,29 +742,33 @@ return 0; }}); } - - bool m_updated{false}; }; TEST(Pointer, simple) { - Context context; - Cat *p{new Cat}; + bool deleted{false}; + Cat *p{new Cat(deleted)}; - ASSERT_EQ(0, context.top()); - context.push(Pointer<Cat>{p}); - Cat *p2 = context.get<Pointer<Cat>>(-1); - ASSERT_EQ(1, context.top()); + { + Context context; + + ASSERT_EQ(0, context.top()); + context.push(Pointer<Cat>{p}); + Cat *p2 = context.get<Pointer<Cat>>(-1); + ASSERT_EQ(1, context.top()); + + p2->m_updated = true; + ASSERT_TRUE(p->m_updated); + } - p2->m_updated = true; - - ASSERT_TRUE(p->m_updated); + ASSERT_TRUE(deleted); } TEST(Pointer, self) { + bool deleted; Context ctx; - Cat *p{new Cat}; + Cat *p{new Cat(deleted)}; try { ctx.putGlobal("player", Pointer<Cat>{p});