Mercurial > code
changeset 392:69adcefe73ae
Js: make it object-oriented
author | David Demelier <markand@malikania.fr> |
---|---|
date | Mon, 28 Sep 2015 19:40:16 +0200 |
parents | e2cefd0ee511 |
children | 30788c97c58c |
files | C++/modules/Js/Js.cpp C++/modules/Js/Js.h C++/tests/Js/main.cpp |
diffstat | 3 files changed, 688 insertions(+), 774 deletions(-) [+] |
line wrap: on
line diff
--- a/C++/modules/Js/Js.cpp Mon Sep 28 13:18:14 2015 +0200 +++ b/C++/modules/Js/Js.cpp Mon Sep 28 19:40:16 2015 +0200 @@ -56,151 +56,42 @@ } // !namespace -/* ------------------------------------------------------------------ - * Context class - * ------------------------------------------------------------------ */ - -Context::Context() - : std::unique_ptr<duk_context, void (*)(duk_context *)>(duk_create_heap_default(), duk_destroy_heap) -{ -} - -/* -------------------------------------------------------- - * Push functions - * -------------------------------------------------------- */ - -void push(ContextPtr ctx, bool value) -{ - duk_push_boolean(ctx, value); -} - -void push(ContextPtr ctx, int value) -{ - duk_push_int(ctx, value); -} - -void push(ContextPtr ctx, double value) -{ - duk_push_number(ctx, value); -} - -void push(ContextPtr ctx, const std::string &value) -{ - duk_push_lstring(ctx, value.c_str(), value.length()); -} - -void push(ContextPtr ctx, const char *value) -{ - duk_push_string(ctx, value); -} - -void push(ContextPtr ctx, const Function &fn) -{ - assert(fn.function); - - duk_push_c_function(ctx, fn.function, fn.nargs); -} - -void push(ContextPtr ctx, const Object &) -{ - duk_push_object(ctx); -} - -void push(ContextPtr ctx, const Array &) -{ - duk_push_array(ctx); -} - -/* -------------------------------------------------------- - * Get functions - * -------------------------------------------------------- */ - -void get(ContextPtr ctx, duk_idx_t index, bool &value) -{ - value = duk_get_boolean(ctx, index); -} - -void get(ContextPtr ctx, duk_idx_t index, int &value) -{ - value = duk_get_int(ctx, index); -} - -void get(ContextPtr ctx, duk_idx_t index, double &value) -{ - value = duk_get_number(ctx, index); -} - -void get(ContextPtr ctx, duk_idx_t index, std::string &value) -{ - duk_size_t size; - const char *text = duk_get_lstring(ctx, index, &size); - - value = std::string{text, size}; -} - -/* -------------------------------------------------------- - * Require functions - * -------------------------------------------------------- */ - -void require(ContextPtr ctx, duk_idx_t index, bool &value) -{ - value = duk_require_boolean(ctx, index); -} - -void require(ContextPtr ctx, duk_idx_t index, int &value) -{ - value = duk_require_int(ctx, index); -} - -void require(ContextPtr ctx, duk_idx_t index, double &value) -{ - value = duk_require_number(ctx, index); -} - -void require(ContextPtr ctx, duk_idx_t index, std::string &value) -{ - duk_size_t size; - const char *text = duk_require_lstring(ctx, index, &size); - - value = std::string{text, size}; -} - /* -------------------------------------------------------- * Basic functions * -------------------------------------------------------- */ -duk_int_t type(ContextPtr ctx, duk_idx_t index) +duk_int_t Context::type(duk_idx_t index) { - return duk_get_type(ctx, index); + return duk_get_type(m_handle.get(), index); } -int top(ContextPtr ctx) +int Context::top() { - return duk_get_top(ctx); + return duk_get_top(m_handle.get()); } -void pop(ContextPtr ctx, int count) +void Context::pop(int count) { assert(count > 0); - duk_pop_n(ctx, count); + duk_pop_n(m_handle.get(), count); } -duk_bool_t instanceof(ContextPtr ctx, duk_idx_t idx1, duk_idx_t idx2) +duk_bool_t Context::instanceof(duk_idx_t idx1, duk_idx_t idx2) { - return duk_instanceof(ctx, idx1, idx2); + return duk_instanceof(m_handle.get(), idx1, idx2); } -void call(ContextPtr ctx, duk_idx_t nargs) +void Context::call(duk_idx_t nargs) { - duk_call(ctx, nargs); + duk_call(m_handle.get(), nargs); } -void pcall(ContextPtr ctx, duk_idx_t nargs) +void Context::pcall(duk_idx_t nargs) { - if (duk_pcall(ctx, nargs) != 0) { - ErrorInfo info = error(ctx, -1); - pop(ctx); + if (duk_pcall(m_handle.get(), nargs) != 0) { + ErrorInfo info = error(m_handle.get(), -1); + pop(); throw info; } @@ -210,43 +101,43 @@ * Eval functions * ------------------------------------------------------------------ */ -void eval(ContextPtr ctx) +void Context::eval() { - duk_eval(ctx); + duk_eval(m_handle.get()); } -void evalString(ContextPtr ctx, const std::string &name) +void Context::evalString(const std::string &name) { - duk_eval_string(ctx, name.c_str()); + duk_eval_string(m_handle.get(), name.c_str()); } -void evalFile(ContextPtr ctx, const std::string &file) +void Context::evalFile(const std::string &file) { - duk_eval_file(ctx, file.c_str()); + duk_eval_file(m_handle.get(), file.c_str()); } -void peval(ContextPtr ctx) +void Context::peval() { - if (duk_peval(ctx) != 0) { - throw error(ctx, -1); + if (duk_peval(m_handle.get()) != 0) { + throw error(m_handle.get(), -1); } } -void pevalString(ContextPtr ctx, const std::string &script) +void Context::pevalString(const std::string &script) { - if (duk_peval_string(ctx, script.c_str()) != 0) { - ErrorInfo info = error(ctx, -1); - pop(ctx); + if (duk_peval_string(m_handle.get(), script.c_str()) != 0) { + ErrorInfo info = error(m_handle.get(), -1); + pop(); throw info; } } -void pevalFile(ContextPtr ctx, const std::string &file) +void Context::pevalFile(const std::string &file) { - if (duk_peval_file(ctx, file.c_str()) != 0) { - ErrorInfo info = error(ctx, -1); - pop(ctx); + if (duk_peval_file(m_handle.get(), file.c_str()) != 0) { + ErrorInfo info = error(m_handle.get(), -1); + pop(); throw info; }
--- a/C++/modules/Js/Js.h Mon Sep 28 13:18:14 2015 +0200 +++ b/C++/modules/Js/Js.h Mon Sep 28 19:40:16 2015 +0200 @@ -68,47 +68,6 @@ using ContextPtr = duk_context *; /** - * @class Context - * @brief RAII based Duktape handler - * - * This class is implicitly convertible to duk_context for convenience. - */ -class Context : public std::unique_ptr<duk_context, void (*)(duk_context *)> { -private: - /* Move and copy forbidden */ - Context(const Context &) = delete; - Context &operator=(const Context &) = delete; - Context(const Context &&) = delete; - Context &operator=(const Context &&) = delete; - -public: - /** - * Create a Duktape context using defaults. - */ - Context(); - - /** - * Convert the context to the native Duktape/C type. - * - * @return the duk_context - */ - inline operator duk_context *() noexcept - { - return get(); - } - - /** - * Convert the context to the native Duktape/C type. - * - * @return the duk_context - */ - inline operator duk_context *() const noexcept - { - return get(); - } -}; - -/** * @class Object * @brief Empty class tag for push() function */ @@ -164,398 +123,515 @@ } }; -/* -------------------------------------------------------- - * Push functions - * -------------------------------------------------------- */ - -/** - * Push a boolean. - * - * @param ctx the context - * @param value the boolean value - */ -void push(ContextPtr ctx, bool value); - -/** - * Push an integer. - * - * @param ctx the context - * @param value the integer value - */ -void push(ContextPtr ctx, int value); - -/** - * Push a real value. - * - * @param ctx the context - * @param value the value - */ -void push(ContextPtr ctx, double value); - -/** - * Push a string. - * - * @param ctx the context - * @param value the string - */ -void push(ContextPtr ctx, const std::string &value); - -/** - * Push a literal or C-string. - * - * @param ctx the context - * @param value the value - */ -void push(ContextPtr ctx, const char *value); - -/** - * Push a Duktape/C function. - * - * @param ctx the context - * @param function the function - */ -void push(ContextPtr ctx, const Function &function); - -/** - * Push an empty object on the stack. - * - * @param ctx the context - * @param object the empty object - */ -void push(ContextPtr ctx, const Object &object); - -/** - * Push an empty array at the top of the stack. - * - * @param ctx the context - * @param array the empty array - */ -void push(ContextPtr ctx, const Array &array); - -/* -------------------------------------------------------- - * Get functions - * -------------------------------------------------------- */ - -/** - * Get a boolean. - * - * @param ctx the context - * @param index the index - * @param value the reference where to store the value - */ -void get(ContextPtr ctx, duk_idx_t index, bool &value); - -/** - * Get an integer. - * - * @param ctx the context - * @param index the index - * @param value the reference where to store the value - */ -void get(ContextPtr ctx, duk_idx_t index, int &value); - -/** - * Get a real. - * - * @param ctx the context - * @param index the index - * @param value the reference where to store the value - */ -void get(ContextPtr ctx, duk_idx_t index, double &value); - -/** - * Get a string. - * - * @param ctx the context - * @param index the index - * @param value the reference where to store the value - */ -void get(ContextPtr ctx, duk_idx_t index, std::string &value); - -/** - * Generic template function to get a value from the stack. - * - * @param ctx the context - * @param index the index - * @return the value - */ template <typename Type> -inline Type get(ContextPtr ctx, duk_idx_t index) -{ - Type value; - - get(ctx, index, value); - - return value; -} - -/* -------------------------------------------------------- - * Get functions (for object) - * -------------------------------------------------------- */ - -template <typename Type> -inline Type getObject(ContextPtr ctx, duk_idx_t index, const std::string &name) -{ - Type type; - - assertBegin(ctx); - duk_get_prop_string(ctx, index, name.c_str()); - get(ctx, -1, type); - duk_pop(ctx); - assertEquals(ctx); - - return type; -} - -/* -------------------------------------------------------- - * Set functions (for object) - * -------------------------------------------------------- */ - -template <typename Type> -void setObject(ContextPtr ctx, duk_idx_t index, const std::string &name, Type&& value) -{ - index = duk_normalize_index(ctx, index); - - assertBegin(ctx); - push(ctx, std::forward<Type>(value)); - duk_put_prop_string(ctx, index, name.c_str()); - assertEquals(ctx); -} - -/* -------------------------------------------------------- - * Require functions - * -------------------------------------------------------- */ - -/** - * Requires a value or throw an exception if not a boolean. - * - * @param ctx the context - * @param index the index - * @param value the reference where to store the value - */ -void require(ContextPtr ctx, duk_idx_t index, bool &value); - -/** - * Requires a value or throw an exception if not an integer. - * - * @param ctx the context - * @param index the index - * @param value the reference where to store the value - */ -void require(ContextPtr ctx, duk_idx_t index, int &value); - -/** - * Requires a value or throw an exception if not a double. - * - * @param ctx the context - * @param index the index - * @param value the reference where to store the value - */ -void require(ContextPtr ctx, duk_idx_t index, double &value); +class TypeInfo { +public: +}; /** - * Requires a value or throw an exception if not a string. - * - * @param ctx the context - * @param index the index - * @param value the reference where to store string - */ -void require(ContextPtr ctx, duk_idx_t index, std::string &value); - -/** - * Generic function to require a value from the stack. - * - * @param ctx the context - * @param index the specified index - * @return the value - */ -template <typename Type> -inline Type require(ContextPtr ctx, duk_idx_t index) -{ - Type value; - - require(ctx, index, value); - - return value; -} - -/* -------------------------------------------------------- - * Basic functions - * -------------------------------------------------------- */ - -/** - * 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(ContextPtr ctx, duk_idx_t index); - -/** - * Get the current stack size. - * - * @param ctx the context - * @return the stack size - */ -int top(ContextPtr ctx); - -/** - * 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(ContextPtr ctx, int count = 1); - -/** - * 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 - */ -duk_bool_t instanceof(ContextPtr ctx, duk_idx_t idx1, duk_idx_t idx2); - -/** - * Call the object at the top of the stack. - * - * @param ctx the context - * @param nargs the number of arguments - * @note Non-protected - */ -void call(ContextPtr ctx, duk_idx_t nargs = 0); - -/** - * Call in protected mode the object at the top of the stack. + * @class Context + * @brief RAII based Duktape handler * - * @param ctx the context - * @param nargs the number of arguments - * @throw ErrorInfo on errors + * This class is implicitly convertible to duk_context for convenience. */ -void pcall(ContextPtr ctx, duk_idx_t nargs = 0); +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: + inline Context() + : m_handle{duk_create_heap_default(), duk_destroy_heap} + { + } + + inline Context(ContextPtr ctx) noexcept + : m_handle{ctx, [] (ContextPtr) {}} + { + } -/* ------------------------------------------------------------------ - * Eval functions - * ------------------------------------------------------------------ */ +#if 0 + /** + * Convert the context to the native Duktape/C type. + * + * @return the duk_context + */ + inline operator duk_context *() noexcept + { + return get(); + } -/** - * Evaluate a non-protected chunk at the top of the stack. - * - * @param ctx - */ -void eval(ContextPtr ctx); + /** + * Convert the context to the native Duktape/C type. + * + * @return the duk_context + */ + inline operator duk_context *() const noexcept + { + return get(); + } +#endif + + /* -------------------------------------------------------- + * Push functions + * -------------------------------------------------------- */ + +#if 0 -/** - * Evaluate a non-protected string script. - * - * @param ctx the context - * @param script the script content - */ -void evalString(ContextPtr ctx, const std::string &script); + /** + * Push a boolean. + * + * @param ctx the context + * @param value the boolean value + */ + inline void push(bool value) + { + duk_push_boolean(m_handle.get(), value); + } + + /** + * Push an integer. + * + * @param ctx the context + * @param value the integer value + */ + inline void push(int value) + { + duk_push_int(m_handle.get(), value); + } -/** - * Evaluate a non-protected file. - * - * @param ctx the context - * @param file the file - */ -void evalFile(ContextPtr ctx, const std::string &file); + /** + * Push a real value. + * + * @param ctx the context + * @param value the value + */ + void push(double value) + { + duk_push_number(m_handle.get(), value); + } -/** - * Evaluate a protected chunk. - * - * @param ctx the context - * @throw ErrorInfo the error - */ -void peval(ContextPtr ctx); + /** + * Push a string. + * + * @param ctx the context + * @param value the string + */ + void push(const std::string &value) + { + duk_push_lstring(m_handle.get(), value.c_str(), value.length()); + } + + /** + * Push a literal or C-string. + * + * @param ctx the context + * @param value the value + */ + void push(const char *value) + { + duk_push_string(m_handle.get(), value); + } + + /** + * Push a Duktape/C function. + * + * @param ctx the context + * @param function the function + */ + void push(const Function &fn) + { + assert(fn.function); -/** - * Evaluate a protected string script. - * - * @param ctx the context - * @param script the script content - * @throw ErrorInfo the error - */ -void pevalString(ContextPtr ctx, const std::string &script); + duk_push_c_function(m_handle.get(), fn.function, fn.nargs); + } + + /** + * Push an empty object on the stack. + * + * @param ctx the context + * @param object the empty object + */ + void push(const Object &) + { + duk_push_object(m_handle.get()); + } + + /** + * Push an empty array at the top of the stack. + * + * @param ctx the context + * @param array the empty array + */ + void push(const Array &) + { + duk_push_array(m_handle.get()); + } -/** - * Evaluate a protected file. - * - * @param ctx the context - * @param file the file - * @throw ErrorInfo the error - */ -void pevalFile(ContextPtr ctx, const std::string &file); + /* -------------------------------------------------------- + * Get functions + * -------------------------------------------------------- */ + + /** + * Get a boolean. + * + * @param ctx the context + * @param index the index + * @param value the reference where to store the value + */ + void get(ContextPtr ctx, duk_idx_t index, bool &value); + + /** + * Get an integer. + * + * @param ctx the context + * @param index the index + * @param value the reference where to store the value + */ + void get(ContextPtr ctx, duk_idx_t index, int &value); + + /** + * Get a real. + * + * @param ctx the context + * @param index the index + * @param value the reference where to store the value + */ + void get(ContextPtr ctx, duk_idx_t index, double &value); -/* ------------------------------------------------------------------ - * Extended functions - * ------------------------------------------------------------------ */ + /** + * Get a string. + * + * @param ctx the context + * @param index the index + * @param value the reference where to store the value + */ + void get(ContextPtr ctx, duk_idx_t index, std::string &value); + +#endif + + /* ---------------------------------------------------------- + * Push / Get / Require + * ---------------------------------------------------------- */ + + template <typename Type> + inline void push(Type &&value) + { + TypeInfo<std::decay_t<Type>>::push(m_handle.get(), 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 Type get(duk_idx_t index) + { + return TypeInfo<Type>::get(m_handle.get(), index); + } -/** - * Enumerate an object or an array at the specified index. - * - * @param ctx the context - * @param index the object or array index - * @param flags the optional flags to pass to duk_enum - * @param getvalue set to true if you want to extract the value - * @param func the function to call for each properties - */ -template <typename Func> -void enumerate(ContextPtr ctx, duk_idx_t index, duk_uint_t flags, duk_bool_t getvalue, Func&& func) -{ - assertBegin(ctx); - duk_enum(ctx, index, flags); + template <typename Type> + inline Type require(duk_idx_t index) + { + return TypeInfo<Type>::require(m_handle.get(), index); + } + + /* -------------------------------------------------------- + * Get functions (for object) + * -------------------------------------------------------- */ - while (duk_next(ctx, -1, getvalue)) { - func(ctx); - duk_pop_n(ctx, 1 + (getvalue ? 1 : 0)); + template <typename Type> + inline Type getObject(duk_idx_t index, const std::string &name) + { + assertBegin(m_handle.get()); + duk_get_prop_string(m_handle.get(), index, name.c_str()); + Type value = get<Type>(-1); + duk_pop(m_handle.get()); + assertEquals(m_handle.get()); + + return value; + } + + /* -------------------------------------------------------- + * Set functions (for object) + * -------------------------------------------------------- */ + + template <typename Type> + void setObject(duk_idx_t 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()); } - duk_pop(ctx); - assertEquals(ctx); -} + /* -------------------------------------------------------- + * Require functions + * -------------------------------------------------------- */ + +#if 0 + + /** + * Requires a value or throw an exception if not a boolean. + * + * @param ctx the context + * @param index the index + * @param value the reference where to store the value + */ + void require(ContextPtr ctx, duk_idx_t index, bool &value); + + /** + * Requires a value or throw an exception if not an integer. + * + * @param ctx the context + * @param index the index + * @param value the reference where to store the value + */ + void require(ContextPtr ctx, duk_idx_t index, int &value); + + /** + * Requires a value or throw an exception if not a double. + * + * @param ctx the context + * @param index the index + * @param value the reference where to store the value + */ + void require(ContextPtr ctx, duk_idx_t index, double &value); + + /** + * Requires a value or throw an exception if not a string. + * + * @param ctx the context + * @param index the index + * @param value the reference where to store string + */ + void require(ContextPtr ctx, duk_idx_t index, std::string &value); + + /** + * Generic function to require a value from the stack. + * + * @param ctx the context + * @param index the specified index + * @return the value + */ + template <typename Type> + inline Type require(ContextPtr ctx, duk_idx_t index) + { + Type value; + + require(ctx, index, value); + + return value; + } +#endif -/* -------------------------------------------------------- - * Global functions - * -------------------------------------------------------- */ + /* -------------------------------------------------------- + * Basic functions + * -------------------------------------------------------- */ + + /** + * 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); + + /** + * Get the current stack size. + * + * @param ctx the context + * @return the stack size + */ + int top(); + + /** + * 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); + + /** + * 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 + */ + duk_bool_t instanceof(duk_idx_t idx1, duk_idx_t idx2); + + /** + * Call the object at the top of the stack. + * + * @param ctx the context + * @param nargs the number of arguments + * @note Non-protected + */ + void call(duk_idx_t nargs = 0); + + /** + * Call in protected mode the object at the top of the stack. + * + * @param ctx the context + * @param nargs the number of arguments + * @throw ErrorInfo on errors + */ + void pcall(duk_idx_t nargs = 0); + + /* ------------------------------------------------------------------ + * Eval functions + * ------------------------------------------------------------------ */ -/** - * Get a global value. - * - * @param ctx the context - * @param name the name of the global variable - * @return the value - */ -template <typename Type> -inline Type getGlobal(ContextPtr ctx, const std::string &name) -{ - assertBegin(ctx); - duk_get_global_string(ctx, name.c_str()); - Type value = get<Type>(ctx, -1); - duk_pop(ctx); - assertEquals(ctx); + /** + * Evaluate a non-protected chunk at the top of the stack. + * + * @param ctx + */ + void eval(); + + /** + * Evaluate a non-protected string script. + * + * @param ctx the context + * @param script the script content + */ + void evalString(const std::string &script); + + /** + * Evaluate a non-protected file. + * + * @param ctx the context + * @param file the file + */ + void evalFile(const std::string &file); + + /** + * Evaluate a protected chunk. + * + * @param ctx the context + * @throw ErrorInfo the error + */ + void peval(); + + /** + * Evaluate a protected string script. + * + * @param ctx the context + * @param script the script content + * @throw ErrorInfo the error + */ + void pevalString(const std::string &script); + + /** + * Evaluate a protected file. + * + * @param ctx the context + * @param file the file + * @throw ErrorInfo the error + */ + void pevalFile(const std::string &file); + + /* ------------------------------------------------------------------ + * Extended functions + * ------------------------------------------------------------------ */ - return value; -} + /** + * Enumerate an object or an array at the specified index. + * + * @param ctx the context + * @param index the object or array index + * @param flags the optional flags to pass to duk_enum + * @param getvalue set to true if you want to extract the value + * @param func the function to call for each properties + */ + template <typename Func> + void enumerate(duk_idx_t 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)) { + func(*this); + duk_pop_n(m_handle.get(), 1 + (getvalue ? 1 : 0)); + } + + duk_pop(m_handle.get()); + assertEquals(m_handle.get()); + } + + /* -------------------------------------------------------- + * Global functions + * -------------------------------------------------------- */ -/** - * Set a global variable. - * - * @param ctx the context - * @param name the name of the global variable - * @param type the value to set - */ -template <typename Type> -inline void setGlobal(ContextPtr ctx, const std::string &name, Type&& type) -{ - assertBegin(ctx); - push(ctx, std::forward<Type>(type)); - duk_put_global_string(ctx, name.c_str()); - assertEquals(ctx); -} + /** + * Get a global value. + * + * @param ctx the context + * @param name the name of the global variable + * @return the value + */ + template <typename Type> + inline Type getGlobal(const std::string &name) + { + assertBegin(m_handle.get()); + duk_get_global_string(m_handle.get(), name.c_str()); + Type value = get<Type>(-1); + duk_pop(m_handle.get()); + assertEquals(m_handle.get()); + + return value; + } + + /** + * Set a global variable. + * + * @param ctx the context + * @param name the name of the global variable + * @param type the value to set + */ + template <typename Type> + inline void 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()); + } + + /** + * Throw an ECMAScript exception. + * + * @param ctx the context + * @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()); + } +}; /* ------------------------------------------------------------------ * Exception handling @@ -659,21 +735,106 @@ URIError(std::string message); }; -/** - * Throw an ECMAScript exception. - * - * @param ctx the context - * @param ex the exception - */ -template <typename Exception> -void raise(ContextPtr ctx, const Exception &ex) -{ - ex.create(ctx); +/* ------------------------------------------------------------------ + * Standard overloads for TypeInfo<T>::get + * ------------------------------------------------------------------ */ + +template <> +class TypeInfo<int> { +public: + static int get(ContextPtr ctx, duk_idx_t index) + { + return duk_get_int(ctx, index); + } + + static void push(ContextPtr ctx, int value) + { + duk_push_int(ctx, value); + } +}; + +template <> +class TypeInfo<bool> { +public: + static bool get(ContextPtr ctx, duk_idx_t index) + { + return duk_get_boolean(ctx, index); + } + + static void push(ContextPtr ctx, bool value) + { + duk_push_boolean(ctx, value); + } +}; + +template <> +class TypeInfo<double> { +public: + static double get(ContextPtr ctx, duk_idx_t index) + { + return duk_get_number(ctx, index); + } + + static void push(ContextPtr ctx, double value) + { + duk_push_number(ctx, value); + } +}; - duk_push_string(ctx, ex.name().c_str()); - duk_put_prop_string(ctx, -2, "name"); - duk_throw(ctx); -} +template <> +class TypeInfo<std::string> { +public: + static std::string get(ContextPtr ctx, duk_idx_t index) + { + duk_size_t size; + const char *text = duk_get_lstring(ctx, index, &size); + + return std::string{text, size}; + } + + static void push(ContextPtr ctx, const std::string &value) + { + duk_push_lstring(ctx, value.c_str(), value.length()); + } +}; + +template <> +class TypeInfo<const char *> { +public: + static void push(ContextPtr ctx, const char *value) + { + duk_push_string(ctx, value); + } +}; + +template <> +class TypeInfo<Function> { +public: + static void push(ContextPtr ctx, const Function &fn) + { + assert(fn.function); + + duk_push_c_function(ctx, fn.function, fn.nargs); + } +}; + +template <> +class TypeInfo<Object> { +public: + static void push(ContextPtr ctx, const Object &) + { + duk_push_object(ctx); + } +}; + +template <> +class TypeInfo<Array> { +public: + static void push(ContextPtr ctx, const Array &) + { + duk_push_array(ctx); + } +}; } // !js
--- a/C++/tests/Js/main.cpp Mon Sep 28 13:18:14 2015 +0200 +++ b/C++/tests/Js/main.cpp Mon Sep 28 19:40:16 2015 +0200 @@ -30,38 +30,38 @@ { Context context; - push(context, true); - ASSERT_TRUE(get<bool>(context, -1)); - push(context, false); - ASSERT_FALSE(get<bool>(context, -1)); + context.push(true); + ASSERT_TRUE(context.get<bool>(-1)); + context.push(false); + ASSERT_FALSE(context.get<bool>(-1)); } TEST(PushAndGet, integer) { Context context; - push(context, 123); - ASSERT_EQ(123, get<int>(context, -1)); - push(context, 456); - ASSERT_EQ(456, get<int>(context, -1)); + context.push(123); + ASSERT_EQ(123, context.get<int>(-1)); + context.push(456); + ASSERT_EQ(456, context.get<int>(-1)); } TEST(PushAndGet, number) { Context context; - push(context, 10.5); - ASSERT_EQ(10.5, get<double>(context, -1)); - push(context, 50.1); - ASSERT_EQ(50.1, get<double>(context, -1)); + context.push(10.5); + ASSERT_EQ(10.5, context.get<double>(-1)); + context.push(50.1); + ASSERT_EQ(50.1, context.get<double>(-1)); } TEST(PushAndGet, string) { Context context; - push(context, "hello world!"); - ASSERT_EQ("hello world!", get<std::string>(context, -1)); + context.push("hello world!"); + ASSERT_EQ("hello world!", context.get<std::string>(-1)); } /* -------------------------------------------------------- @@ -73,69 +73,68 @@ Context context; // boolean - setGlobal(context, "valueBoolean", true); - ASSERT_TRUE(getGlobal<bool>(context, "valueBoolean")); + context.setGlobal("valueBoolean", true); + ASSERT_TRUE(context.getGlobal<bool>("valueBoolean")); // integer - setGlobal(context, "valueInteger", 123); - ASSERT_EQ(123, getGlobal<int>(context, "valueInteger")); + context.setGlobal("valueInteger", 123); + ASSERT_EQ(123, context.getGlobal<int>("valueInteger")); } TEST(Basics, top) { Context context; - int current = top(context); - push(context, true); - ASSERT_EQ(current + 1, top(context)); + int current = context.top(); + context.push(true); + ASSERT_EQ(current + 1, context.top()); } TEST(Basics, pop1) { Context context; - int current = top(context); - push(context, true); - pop(context); - ASSERT_EQ(current, top(context)); + int current = context.top(); + context.push(true); + context.pop(); + ASSERT_EQ(current, context.top()); } TEST(Basics, pop2) { Context context; - int current = top(context); - push(context, true); - push(context, true); - pop(context, 2); - ASSERT_EQ(current, top(context)); + int current = context.top(); + context.push(true); + context.push(true); + context.pop(2); + ASSERT_EQ(current, context.top()); } TEST(Basics, setObject) { Context context; - push(context, Object{}); - setObject(context, -1, "x", 123); - ASSERT_EQ(123, getObject<int>(context, -1, "x")); + context.push(Object{}); + context.setObject(-1, "x", 123); + ASSERT_EQ(123, context.getObject<int>(-1, "x")); } TEST(Basics, enumerate) { Context context; - push(context, Object{}); - setObject(context, -1, "x", 123); - setObject(context, -1, "y", 456); + context.push(Object{}); + context.setObject(-1, "x", 123); + context.setObject(-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)); - enumerate(context, -1, 0, true, [] (ContextPtr ctx) { - ASSERT_EQ(DUK_TYPE_STRING, duk_get_type(ctx, -2)); - ASSERT_EQ(DUK_TYPE_NUMBER, duk_get_type(ctx, -1)); - - if (get<std::string>(ctx, -2) == "x") - ASSERT_EQ(123, get<int>(ctx, -1)); - if (get<std::string>(ctx, -2) == "y") - ASSERT_EQ(456, get<int>(ctx, -1)); + if (ctx.get<std::string>(-2) == "x") + ASSERT_EQ(123, ctx.get<int>(-1)); + if (ctx.get<std::string>(-2) == "y") + ASSERT_EQ(456, ctx.get<int>(-1)); }); } @@ -143,14 +142,16 @@ { Context context; - push(context, Function{[] (ContextPtr ctx) -> duk_ret_t { - setGlobal(ctx, "x", 123); + context.push(Function{[] (ContextPtr pctx) -> duk_ret_t { + Context ctx{pctx}; + + ctx.setGlobal("x", 123); return 0; }}); - call(context); + context.call(); - ASSERT_EQ(123, getGlobal<int>(context, "x")); + ASSERT_EQ(123, context.getGlobal<int>("x")); } /* ------------------------------------------------------------------ @@ -161,29 +162,32 @@ { Context context; - evalString(context, "x = 123;"); - ASSERT_EQ(123, getGlobal<int>(context, "x")); + context.evalString("x = 123;"); + ASSERT_EQ(123, context.getGlobal<int>("x")); } TEST(Eval, function) { Context context; - evalString(context, "function f() { x = 123; }; f();"); - ASSERT_EQ(123, getGlobal<int>(context, "x")); + context.evalString("function f() { x = 123; }; f();"); + ASSERT_EQ(123, context.getGlobal<int>("x")); } TEST(Eval, cfunction) { Context context; - setGlobal(context, "f", Function{[] (ContextPtr ctx) -> duk_idx_t { - setGlobal(ctx, "x", 123); + context.setGlobal("f", Function{[] (ContextPtr pctx) -> duk_idx_t { + Context ctx{pctx}; + + ctx.setGlobal("x", 123); return 0; }}); - evalString(context, "f()"); - ASSERT_EQ(123, getGlobal<int>(context, "x")); + context.evalString("f()"); + + ASSERT_EQ(123, context.getGlobal<int>("x")); } /* ------------------------------------------------------------------ @@ -195,7 +199,7 @@ Context context; try { - pevalString(context, "x = 1"); + context.pevalString("x = 1"); } catch (const ErrorInfo &info) { FAIL() << "error unexpected: " << info.what(); } @@ -206,7 +210,7 @@ Context context; try { - pevalString(context, "doesnotexists()"); + context.pevalString("doesnotexists()"); FAIL() << "expected exception"; } catch (const ErrorInfo &) { @@ -221,60 +225,14 @@ { Context context; - setGlobal(context, "f", Function{[] (ContextPtr ctx) -> duk_idx_t { - raise(ctx, Error{"error thrown"}); + context.setGlobal("f", Function{[] (ContextPtr pctx) -> duk_idx_t { + Context ctx{pctx}; + + ctx.raise(Error{"error thrown"}); return 0; }}); - evalString(context, - "try {" - " f();" - "} catch (ex) {" - " name = ex.name;" - " message = ex.message;" - " received = true;" - "}" - ); - - ASSERT_TRUE(getGlobal<bool>(context, "received")); - ASSERT_EQ("Error", getGlobal<std::string>(context, "name")); - ASSERT_EQ("error thrown", getGlobal<std::string>(context, "message")); -} - -TEST(Exception, evalError) -{ - Context context; - - setGlobal(context, "f", Function{[] (ContextPtr ctx) -> duk_idx_t { - raise(ctx, EvalError{"failed"}); - - return 0; - }}); - evalString(context, - "try {" - " f();" - "} catch (ex) {" - " name = ex.name;" - " message = ex.message;" - " received = true;" - "}" - ); - - ASSERT_TRUE(getGlobal<bool>(context, "received")); - ASSERT_EQ("EvalError", getGlobal<std::string>(context, "name")); - ASSERT_EQ("failed", getGlobal<std::string>(context, "message")); -} - -TEST(Exception, rangeError) -{ - Context context; - - setGlobal(context, "f", Function{[] (ContextPtr ctx) -> duk_idx_t { - raise(ctx, RangeError{"e2big"}); - - return 0; - }}); - evalString(context, + context.evalString( "try {" " f();" "} catch (ex) {" @@ -284,105 +242,9 @@ "}" ); - ASSERT_TRUE(getGlobal<bool>(context, "received")); - ASSERT_EQ("RangeError", getGlobal<std::string>(context, "name")); - ASSERT_EQ("e2big", getGlobal<std::string>(context, "message")); -} - -TEST(Exception, referenceError) -{ - Context context; - - setGlobal(context, "f", Function{[] (ContextPtr ctx) -> duk_idx_t { - raise(ctx, ReferenceError{"does not exists"}); - - return 0; - }}); - evalString(context, - "try {" - " f();" - "} catch (ex) {" - " name = ex.name;" - " message = ex.message;" - " received = true;" - "}" - ); - - ASSERT_TRUE(getGlobal<bool>(context, "received")); - ASSERT_EQ("ReferenceError", getGlobal<std::string>(context, "name")); - ASSERT_EQ("does not exists", getGlobal<std::string>(context, "message")); -} - -TEST(Exception, syntaxError) -{ - Context context; - - setGlobal(context, "f", Function{[] (ContextPtr ctx) -> duk_idx_t { - raise(ctx, SyntaxError{"missing token"}); - - return 0; - }}); - evalString(context, - "try {" - " f();" - "} catch (ex) {" - " name = ex.name;" - " message = ex.message;" - " received = true;" - "}" - ); - - ASSERT_TRUE(getGlobal<bool>(context, "received")); - ASSERT_EQ("SyntaxError", getGlobal<std::string>(context, "name")); - ASSERT_EQ("missing token", getGlobal<std::string>(context, "message")); -} - -TEST(Exception, typeError) -{ - Context context; - - setGlobal(context, "f", Function{[] (ContextPtr ctx) -> duk_idx_t { - raise(ctx, TypeError{"int requested"}); - - return 0; - }}); - evalString(context, - "try {" - " f();" - "} catch (ex) {" - " name = ex.name;" - " message = ex.message;" - " received = true;" - "}" - ); - - ASSERT_TRUE(getGlobal<bool>(context, "received")); - ASSERT_EQ("TypeError", getGlobal<std::string>(context, "name")); - ASSERT_EQ("int requested", getGlobal<std::string>(context, "message")); -} - -TEST(Exception, uriError) -{ - Context context; - - setGlobal(context, "f", Function{[] (ContextPtr ctx) -> duk_idx_t { - raise(ctx, URIError{"invalid scheme"}); - - return 0; - }}); - evalString(context, - "try {" - " f();" - "} catch (ex) {" - " name = ex.name;" - " message = ex.message;" - " received = true;" - "}" - ); - - ASSERT_TRUE(getGlobal<bool>(context, "received")); - ASSERT_EQ("URIError", getGlobal<std::string>(context, "name")); - ASSERT_EQ("invalid scheme", getGlobal<std::string>(context, "message")); + ASSERT_TRUE(context.getGlobal<bool>("received")); + ASSERT_EQ("Error", context.getGlobal<std::string>("name")); + ASSERT_EQ("error thrown", context.getGlobal<std::string>("message")); } int main(int argc, char **argv)