Mercurial > code
changeset 398:94bfe7ba9a13
Js:
- Rename some functions
- Cleanup a lot
- Add more documentation
- Add specialization of getProperty and getGlobal to void to push on the
stack
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 30 Sep 2015 19:59:12 +0200 |
parents | 6b2db5425836 |
children | 73517fd307dd |
files | C++/modules/Js/Js.cpp C++/modules/Js/Js.h C++/tests/Js/main.cpp |
diffstat | 3 files changed, 396 insertions(+), 308 deletions(-) [+] |
line wrap: on
line diff
--- a/C++/modules/Js/Js.cpp Tue Sep 29 12:50:28 2015 +0200 +++ b/C++/modules/Js/Js.cpp Wed Sep 30 19:59:12 2015 +0200 @@ -16,216 +16,51 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <cerrno> -#include <cstring> -#include <fstream> -#include <iterator> -#include <memory> -#include <unordered_map> - #include "Js.h" using namespace std::string_literals; namespace js { -namespace { - -ErrorInfo error(ContextPtr ctx, duk_idx_t index) +ErrorInfo Context::error(int index) { ErrorInfo error; - index = duk_normalize_index(ctx, index); + index = duk_normalize_index(m_handle.get(), index); - assertBegin(ctx); - duk_get_prop_string(ctx, index, "name"); - error.name = duk_to_string(ctx, -1); - duk_get_prop_string(ctx, index, "message"); - error.message = duk_to_string(ctx, -1); - duk_get_prop_string(ctx, index, "fileName"); - error.fileName = duk_to_string(ctx, -1); - duk_get_prop_string(ctx, index, "lineNumber"); - error.lineNumber = duk_to_int(ctx, -1); - duk_get_prop_string(ctx, index, "stack"); - error.stack = duk_to_string(ctx, -1); - duk_pop_n(ctx, 5); - assertEquals(ctx); + 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; } -} // !namespace - -/* -------------------------------------------------------- - * Basic functions - * -------------------------------------------------------- */ - -duk_int_t Context::type(duk_idx_t index) -{ - return duk_get_type(m_handle.get(), index); -} - -int Context::top() -{ - return duk_get_top(m_handle.get()); -} - -void Context::pop(int count) -{ - assert(count > 0); - - duk_pop_n(m_handle.get(), count); -} - -duk_bool_t Context::instanceof(duk_idx_t idx1, duk_idx_t idx2) -{ - return duk_instanceof(m_handle.get(), idx1, idx2); -} - -void Context::call(duk_idx_t nargs) -{ - duk_call(m_handle.get(), nargs); -} - -void Context::pcall(duk_idx_t nargs) +void Context::pcall(unsigned nargs) { if (duk_pcall(m_handle.get(), nargs) != 0) { - ErrorInfo info = error(m_handle.get(), -1); - pop(); + ErrorInfo info = error(-1); + duk_pop(m_handle.get()); throw info; } } -/* ------------------------------------------------------------------ - * Eval functions - * ------------------------------------------------------------------ */ - -void Context::eval() -{ - duk_eval(m_handle.get()); -} - -void Context::evalString(const std::string &name) -{ - duk_eval_string(m_handle.get(), name.c_str()); -} - -void Context::evalFile(const std::string &file) -{ - duk_eval_file(m_handle.get(), file.c_str()); -} - void Context::peval() { if (duk_peval(m_handle.get()) != 0) { - throw error(m_handle.get(), -1); - } -} - -void Context::pevalString(const std::string &script) -{ - if (duk_peval_string(m_handle.get(), script.c_str()) != 0) { - ErrorInfo info = error(m_handle.get(), -1); - pop(); - - throw info; - } -} - -void Context::pevalFile(const std::string &file) -{ - if (duk_peval_file(m_handle.get(), file.c_str()) != 0) { - ErrorInfo info = error(m_handle.get(), -1); - pop(); + ErrorInfo info = error(-1); + duk_pop(m_handle.get()); throw info; } } -/* ------------------------------------------------------------------ - * ExceptionAbstract class - * ------------------------------------------------------------------ */ - -ExceptionAbstract::ExceptionAbstract(std::string name, std::string message) - : m_name(std::move(name)) - , m_message(std::move(message)) -{ -} - -const std::string &ExceptionAbstract::name() const noexcept -{ - return m_name; -} - -void ExceptionAbstract::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); -} - -/* ------------------------------------------------------------------ - * Error class - * ------------------------------------------------------------------ */ - -Error::Error(std::string message) - : ExceptionAbstract("Error", std::move(message)) -{ -} - -/* ------------------------------------------------------------------ - * EvalError class - * ------------------------------------------------------------------ */ - -EvalError::EvalError(std::string message) - : ExceptionAbstract("EvalError", std::move(message)) -{ -} - -/* ------------------------------------------------------------------ - * RangeError class - * ------------------------------------------------------------------ */ - -RangeError::RangeError(std::string message) - : ExceptionAbstract("RangeError", std::move(message)) -{ -} - -/* ------------------------------------------------------------------ - * ReferenceError class - * ------------------------------------------------------------------ */ - -ReferenceError::ReferenceError(std::string message) - : ExceptionAbstract("ReferenceError", std::move(message)) -{ -} - -/* ------------------------------------------------------------------ - * SyntaxError class - * ------------------------------------------------------------------ */ - -SyntaxError::SyntaxError(std::string message) - : ExceptionAbstract("SyntaxError", std::move(message)) -{ -} - -/* ------------------------------------------------------------------ - * TypeError class - * ------------------------------------------------------------------ */ - -TypeError::TypeError(std::string message) - : ExceptionAbstract("TypeError", std::move(message)) -{ -} - -/* ------------------------------------------------------------------ - * URIError class - * ------------------------------------------------------------------ */ - -URIError::URIError(std::string message) - : ExceptionAbstract("URIError", std::move(message)) -{ -} - } // !js
--- a/C++/modules/Js/Js.h Tue Sep 29 12:50:28 2015 +0200 +++ b/C++/modules/Js/Js.h Wed Sep 30 19:59:12 2015 +0200 @@ -30,37 +30,16 @@ */ #include <cassert> -#include <cerrno> -#include <cstring> +#include <functional> #include <memory> -#include <stack> #include <string> -#include <vector> +#include <type_traits> #include <duktape.h> namespace js { -#if !defined(NDEBUG) -#define assertBegin(ctx) \ - int _topstack = duk_get_top(ctx) -#else -#define assertBegin(ctx) -#endif - -#if !defined(NDEBUG) -#define assertEquals(ctx) \ - assert(_topstack == duk_get_top(ctx)) -#else -#define assertEquals(ctx) -#endif - -#if !defined(NDEBUG) -#define assertEnd(ctx, count) \ - assert(_topstack == (duk_get_top(ctx) - count)) -#else -#define assertEnd(ctx, count) -#endif +class Context; /** * Typedef for readability. @@ -90,12 +69,12 @@ /** * The function pointer, must not be null. */ - duk_c_function function{nullptr}; + std::function<int (Context &)> function; /** * Number of args that the function takes */ - duk_idx_t nargs{0}; + int nargs{0}; }; /** @@ -123,9 +102,50 @@ } }; +/** + * @class TypeInfo + * @brief Type information to implement new types in JavaScript's context + */ template <typename Type> class TypeInfo { +}; + +/** + * @class File + * @brief Evaluate script from file + */ +class File { public: + std::string path; + + inline void eval(duk_context *ctx) + { + duk_eval_file(ctx, path.c_str()); + } + + inline int peval(duk_context *ctx) + { + return duk_peval_file(ctx, path.c_str()); + } +}; + +/** + * @class Script + * @brief Evaluate script from raw text + */ +class Script { +public: + std::string text; + + inline void eval(duk_context *ctx) + { + duk_eval_string(ctx, text.c_str()); + } + + inline int peval(duk_context *ctx) + { + return duk_peval_string(ctx, text.c_str()); + } }; /** @@ -147,7 +167,6 @@ Context(const Context &&) = delete; Context &operator=(const Context &&) = delete; - public: /** * Create default context. @@ -209,46 +228,79 @@ * @return the value */ template <typename Type> - inline auto get(duk_idx_t index) -> decltype(TypeInfo<Type>::get(*this, 0)) + 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(duk_idx_t index) -> decltype(TypeInfo<Type>::require(*this, 0)) + inline auto require(int index) -> decltype(TypeInfo<Type>::require(*this, 0)) { return TypeInfo<Type>::require(*this, index); } + // TODO: add optional + /* -------------------------------------------------------- - * Get functions (for object) + * Get properties (for objects) * -------------------------------------------------------- */ + /** + * 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> - inline auto getObject(duk_idx_t index, const std::string &name) -> decltype(get<Type>(0)) + inline auto getProperty(int index, const std::string &name, std::enable_if_t<!std::is_void<Type>::value> * = nullptr) -> decltype(get<Type>(0)) { - assertBegin(m_handle.get()); duk_get_prop_string(m_handle.get(), index, name.c_str()); auto &&value = get<Type>(-1); duk_pop(m_handle.get()); - assertEquals(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> + inline void getProperty(int index, const std::string &name, std::enable_if_t<std::is_void<Type>::value> * = nullptr) + { + duk_get_prop_string(m_handle.get(), index, name.c_str()); + } + /* -------------------------------------------------------- - * Set functions (for object) + * Put properties functions (for object) * -------------------------------------------------------- */ + /** + * 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 setObject(duk_idx_t index, const std::string &name, Type&& value) + void putProperty(int index, const std::string &name, Type&& value) { index = duk_normalize_index(m_handle.get(), index); - assertBegin(m_handle.get()); push(std::forward<Type>(value)); duk_put_prop_string(m_handle.get(), index, name.c_str()); - assertEquals(m_handle.get()); } /* -------------------------------------------------------- @@ -256,13 +308,24 @@ * -------------------------------------------------------- */ /** + * 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); + + /** * Get the type of the value at the specified index. * * @param ctx the context * @param index the idnex * @return the type */ - duk_int_t type(duk_idx_t index); + int type(int index) noexcept + { + return duk_get_type(m_handle.get(), index); + } /** * Get the current stack size. @@ -270,16 +333,21 @@ * @param ctx the context * @return the stack size */ - int top(); + inline int top() noexcept + { + return duk_get_top(m_handle.get()); + } /** * Pop a certain number of values from the top of the stack. * * @param ctx the context * @param count the number of values to pop - * @pre count must be positive */ - void pop(int count = 1); + inline void pop(unsigned count = 1) + { + duk_pop_n(m_handle.get(), count); + } /** * Check if idx1 is an instance of idx2. @@ -289,7 +357,10 @@ * @param idx2 the instance requested * @return true if idx1 is instance of idx2 */ - duk_bool_t instanceof(duk_idx_t idx1, duk_idx_t idx2); + inline bool instanceof(int idx1, int idx2) + { + return duk_instanceof(m_handle.get(), idx1, idx2); + } /** * Call the object at the top of the stack. @@ -298,7 +369,10 @@ * @param nargs the number of arguments * @note Non-protected */ - void call(duk_idx_t nargs = 0); + inline void call(unsigned nargs = 0) + { + duk_call(m_handle.get(), nargs); + } /** * Call in protected mode the object at the top of the stack. @@ -307,37 +381,37 @@ * @param nargs the number of arguments * @throw ErrorInfo on errors */ - void pcall(duk_idx_t nargs = 0); + void pcall(unsigned nargs = 0); /* ------------------------------------------------------------------ * Eval functions * ------------------------------------------------------------------ */ /** - * Evaluate a non-protected chunk at the top of the stack. + * Evaluate a non-protected chunk that is at the top of the stack. * * @param ctx */ - void eval(); + inline void eval() + { + duk_eval(m_handle.get()); + } /** - * Evaluate a non-protected string script. + * Evaluate a non-protected source. * - * @param ctx the context - * @param script the script content + * @param source the source + * @see File + * @see Script */ - void evalString(const std::string &script); + template <typename Source> + inline void eval(Source &&source) + { + source.eval(m_handle.get()); + } /** - * Evaluate a non-protected file. - * - * @param ctx the context - * @param file the file - */ - void evalFile(const std::string &file); - - /** - * Evaluate a protected chunk. + * Evaluate a protected chunk that is at the top of the stack. * * @param ctx the context * @throw ErrorInfo the error @@ -345,22 +419,23 @@ void peval(); /** - * Evaluate a protected string script. + * Evaluate a protected source. * - * @param ctx the context - * @param script the script content - * @throw ErrorInfo the error + * @param source the source + * @see File + * @see Script + * @throw ErrorInfo on failure */ - void pevalString(const std::string &script); + template <typename Source> + inline void peval(Source &&source) + { + if (source.peval(m_handle.get()) != 0) { + ErrorInfo info = error(-1); + duk_pop(m_handle.get()); - /** - * Evaluate a protected file. - * - * @param ctx the context - * @param file the file - * @throw ErrorInfo the error - */ - void pevalFile(const std::string &file); + throw info; + } + } /* ------------------------------------------------------------------ * Extended functions @@ -376,9 +451,8 @@ * @param func the function to call for each properties */ template <typename Func> - void enumerate(duk_idx_t index, duk_uint_t flags, duk_bool_t getvalue, Func&& func) + void enumerate(int index, duk_uint_t flags, duk_bool_t getvalue, Func&& func) { - assertBegin(m_handle.get()); duk_enum(m_handle.get(), index, flags); while (duk_next(m_handle.get(), -1, getvalue)) { @@ -387,7 +461,6 @@ } duk_pop(m_handle.get()); - assertEquals(m_handle.get()); } /* -------------------------------------------------------- @@ -402,18 +475,25 @@ * @return the value */ template <typename Type> - inline auto getGlobal(const std::string &name) -> decltype(get<Type>(0)) + inline auto getGlobal(const std::string &name, std::enable_if_t<!std::is_void<Type>::value> * = nullptr) -> decltype(get<Type>(0)) { - assertBegin(m_handle.get()); duk_get_global_string(m_handle.get(), name.c_str()); auto &&value = get<Type>(-1); duk_pop(m_handle.get()); - assertEquals(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 ctx the context @@ -423,10 +503,8 @@ template <typename Type> inline void setGlobal(const std::string &name, Type&& type) { - assertBegin(m_handle.get()); push(std::forward<Type>(type)); duk_put_global_string(m_handle.get(), name.c_str()); - assertEquals(m_handle.get()); } /** @@ -445,6 +523,11 @@ duk_throw(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)) { @@ -454,6 +537,22 @@ return value; } + + /** + * 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::remove_reference_t<T>>::construct(*this, std::forward<T>(value)); + } }; /* ------------------------------------------------------------------ @@ -477,14 +576,21 @@ * @param name the name (e.g TypeError) * @param message the message */ - ExceptionAbstract(std::string name, std::string message); + inline ExceptionAbstract(std::string name, std::string message) noexcept + : m_name(std::move(name)) + , m_message(std::move(message)) + { + } /** * Get the exception type name. * * @return the exception type */ - const std::string &name() const noexcept; + inline const std::string &name() const noexcept + { + return m_name; + } /** * Create the exception on the stack. @@ -492,7 +598,12 @@ * @note the default implementation search for the global variables * @param ctx the context */ - virtual void create(ContextPtr ctx) const noexcept; + 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); + } }; /** @@ -501,7 +612,10 @@ */ class Error : public ExceptionAbstract { public: - Error(std::string message); + inline Error(std::string message) noexcept + : ExceptionAbstract("Error", std::move(message)) + { + } }; /** @@ -510,7 +624,10 @@ */ class EvalError : public ExceptionAbstract { public: - EvalError(std::string message); + inline EvalError(std::string message) noexcept + : ExceptionAbstract("EvalError", std::move(message)) + { + } }; /** @@ -519,7 +636,10 @@ */ class RangeError : public ExceptionAbstract { public: - RangeError(std::string message); + inline RangeError(std::string message) noexcept + : ExceptionAbstract("RangeError", std::move(message)) + { + } }; /** @@ -528,7 +648,10 @@ */ class ReferenceError : public ExceptionAbstract { public: - ReferenceError(std::string message); + inline ReferenceError(std::string message) noexcept + : ExceptionAbstract("ReferenceError", std::move(message)) + { + } }; /** @@ -537,7 +660,10 @@ */ class SyntaxError : public ExceptionAbstract { public: - SyntaxError(std::string message); + inline SyntaxError(std::string message) noexcept + : ExceptionAbstract("SyntaxError", std::move(message)) + { + } }; /** @@ -546,7 +672,10 @@ */ class TypeError : public ExceptionAbstract { public: - TypeError(std::string message); + inline TypeError(std::string message) noexcept + : ExceptionAbstract("TypeError", std::move(message)) + { + } }; /** @@ -555,17 +684,26 @@ */ class URIError : public ExceptionAbstract { public: - URIError(std::string message); + inline URIError(std::string message) noexcept + : ExceptionAbstract("URIError", std::move(message)) + { + } }; /* ------------------------------------------------------------------ * Standard overloads for TypeInfo<T>::get * ------------------------------------------------------------------ */ +/** + * @class TypeInfo<int> + * @brief Default implementation for int + * + * Provides: get, push, require, optional + */ template <> class TypeInfo<int> { public: - static int get(ContextPtr ctx, duk_idx_t index) + static int get(ContextPtr ctx, int index) { return duk_get_int(ctx, index); } @@ -576,10 +714,16 @@ } }; +/** + * @class TypeInfo<bool> + * @brief Default implementation for bool + * + * Provides: get, push, require, optional + */ template <> class TypeInfo<bool> { public: - static bool get(Context &ctx, duk_idx_t index) + static bool get(Context &ctx, int index) { return duk_get_boolean(ctx, index); } @@ -590,10 +734,16 @@ } }; +/** + * @class TypeInfo<double> + * @brief Default implementation for double + * + * Provides: get, push, require, optional + */ template <> class TypeInfo<double> { public: - static double get(Context &ctx, duk_idx_t index) + static double get(Context &ctx, int index) { return duk_get_number(ctx, index); } @@ -604,10 +754,18 @@ } }; +/** + * @class TypeInfo<std::string> + * @brief Default implementation for std::string + * + * Provides: get, push, require, optional + * + * Note: the functions allows embedded '\0'. + */ template <> class TypeInfo<std::string> { public: - static std::string get(Context &ctx, duk_idx_t index) + static std::string get(Context &ctx, int index) { duk_size_t size; const char *text = duk_get_lstring(ctx, index, &size); @@ -621,6 +779,12 @@ } }; +/** + * @class TypeInfo<const char *> + * @brief Default implementation for const char literals + * + * Provides: push + */ template <> class TypeInfo<const char *> { public: @@ -630,17 +794,69 @@ } }; +/** + * @class TypeInfo<Function> + * @brief Default implementation for Function + * + * Provides: push + * + * This implementation push a Duktape/C function that is wrapped as C++ for convenience. + */ template <> class TypeInfo<Function> { public: - static void push(Context &ctx, const Function &fn) + static void push(Context &ctx, Function fn) { assert(fn.function); - duk_push_c_function(ctx, fn.function, fn.nargs); + /* 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); } }; +/** + * @class TypeInfo<Object> + * @brief Default implementation for Object + * + * Provides: push + * + * This implementation is provided to push empty object into the stack. + */ template <> class TypeInfo<Object> { public: @@ -650,6 +866,14 @@ } }; +/** + * @class TypeInfo<Array> + * @brief Default implementation for Array + * + * Provides: push + * + * This implementation is provided to push empty array into the stack. + */ template <> class TypeInfo<Array> { public: @@ -665,39 +889,72 @@ /** * @class TypeInfoShared - * @brief Generates push / get / require for std::shared_ptr<T> + * @brief Generates push / get / require / optional / construct for std::shared_ptr * * Specialize TypeInfo<std::shared_ptr<T>> and inherits from this class to implement the push(), - * get() and require() functions. + * get(), require() and optional() functions. * * You only need to implement `static void prototype(Context &ctx)` which must push the prototype * to use for the underlying object. + * + * This class can be used to both push and construct shared_ptr objects. */ template <typename T> class TypeInfoShared { +private: + static void apply(Context &ctx, std::shared_ptr<T> value); + public: + static void construct(Context &ctx, std::shared_ptr<T> value); static void push(Context &ctx, std::shared_ptr<T> value); - static std::shared_ptr<T> get(Context &ctx, duk_idx_t index); + static std::shared_ptr<T> get(Context &ctx, int index); }; template <typename T> -void TypeInfoShared<T>::push(Context &ctx, std::shared_ptr<T> value) +void TypeInfoShared<T>::apply(Context &ctx, std::shared_ptr<T> value) { - duk_push_object(ctx); 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"); TypeInfo<std::shared_ptr<T>>::prototype(ctx); + duk_set_prototype(ctx, -2); + duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t { + duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted"); - // TODO: set deleter + 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_set_prototype(ctx, -2); + duk_pop(ctx); + + return 0; + }, 1); + duk_set_finalizer(ctx, -2); } template <typename T> -std::shared_ptr<T> TypeInfoShared<T>::get(Context &ctx, duk_idx_t index) +void TypeInfoShared<T>::construct(Context &ctx, std::shared_ptr<T> value) +{ + duk_push_this(ctx); + apply(ctx, std::move(value)); + duk_pop(ctx); +} + +template <typename T> +void TypeInfoShared<T>::push(Context &ctx, std::shared_ptr<T> value) +{ + duk_push_object(ctx); + apply(ctx, std::move(value)); +} + +template <typename T> +std::shared_ptr<T> TypeInfoShared<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));
--- a/C++/tests/Js/main.cpp Tue Sep 29 12:50:28 2015 +0200 +++ b/C++/tests/Js/main.cpp Wed Sep 30 19:59:12 2015 +0200 @@ -111,13 +111,13 @@ ASSERT_EQ(current, context.top()); } -TEST(Basics, setObject) +TEST(Basics, putProperty) { Context context; context.push(Object{}); - context.setObject(-1, "x", 123); - ASSERT_EQ(123, context.getObject<int>(-1, "x")); + context.putProperty(-1, "x", 123); + ASSERT_EQ(123, context.getProperty<int>(-1, "x")); } TEST(Basics, enumerate) @@ -125,8 +125,8 @@ Context context; context.push(Object{}); - context.setObject(-1, "x", 123); - context.setObject(-1, "y", 456); + context.putProperty(-1, "x", 123); + context.putProperty(-1, "y", 456); context.enumerate(-1, 0, true, [] (Context &ctx) { ASSERT_EQ(DUK_TYPE_STRING, ctx.type(-2)); ASSERT_EQ(DUK_TYPE_NUMBER, ctx.type(-1)); @@ -142,9 +142,7 @@ { Context context; - context.push(Function{[] (ContextPtr pctx) -> duk_ret_t { - Context ctx{pctx}; - + context.push(Function{[] (Context &ctx) -> int { ctx.setGlobal("x", 123); return 0; @@ -162,7 +160,7 @@ { Context context; - context.evalString("x = 123;"); + context.eval(Script{"x = 123;"}); ASSERT_EQ(123, context.getGlobal<int>("x")); } @@ -170,7 +168,7 @@ { Context context; - context.evalString("function f() { x = 123; }; f();"); + context.eval(Script{"function f() { x = 123; }; f();"}); ASSERT_EQ(123, context.getGlobal<int>("x")); } @@ -178,14 +176,12 @@ { Context context; - context.setGlobal("f", Function{[] (ContextPtr pctx) -> duk_idx_t { - Context ctx{pctx}; - + context.setGlobal("f", Function{[] (Context &ctx) -> int { ctx.setGlobal("x", 123); return 0; }}); - context.evalString("f()"); + context.eval(Script{"f()"}); ASSERT_EQ(123, context.getGlobal<int>("x")); } @@ -199,7 +195,7 @@ Context context; try { - context.pevalString("x = 1"); + context.peval(Script{"x = 1"}); } catch (const ErrorInfo &info) { FAIL() << "error unexpected: " << info.what(); } @@ -210,10 +206,10 @@ Context context; try { - context.pevalString("doesnotexists()"); + context.peval(Script{"doesnotexists()"}); FAIL() << "expected exception"; - } catch (const ErrorInfo &) { + } catch (const std::exception &) { } } @@ -232,7 +228,7 @@ return 0; }}); - context.evalString( + context.eval(Script{ "try {" " f();" "} catch (ex) {" @@ -240,7 +236,7 @@ " message = ex.message;" " received = true;" "}" - ); + }); ASSERT_TRUE(context.getGlobal<bool>("received")); ASSERT_EQ("Error", context.getGlobal<std::string>("name")); @@ -265,7 +261,7 @@ ctx.push(Object{}); /* "update" method */ - ctx.setObject(-1, "update", Function{[] (ContextPtr pctx) -> duk_ret_t { + ctx.putProperty(-1, "update", Function{[] (ContextPtr pctx) -> duk_ret_t { Context{pctx}.self<std::shared_ptr<Player>>()->m_updated = true; return 0; @@ -293,7 +289,7 @@ try { ctx.setGlobal("player", p); - ctx.pevalString("player.update();"); + ctx.peval(Script{"player.update();"}); ASSERT_TRUE(p->m_updated); } catch (const std::exception &ex) {