Mercurial > code
changeset 400:c118df15d354
Js:
- Add Context::is,
- Add more documentation,
- Add Null, Undefined tags.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 01 Oct 2015 21:30:57 +0200 |
parents | 73517fd307dd |
children | ca5e4360f79a |
files | C++/modules/Js/Js.h C++/tests/Js/main.cpp |
diffstat | 2 files changed, 349 insertions(+), 111 deletions(-) [+] |
line wrap: on
line diff
--- a/C++/modules/Js/Js.h Thu Oct 01 14:02:28 2015 +0200 +++ b/C++/modules/Js/Js.h Thu Oct 01 21:30:57 2015 +0200 @@ -49,28 +49,42 @@ /** * @class Object - * @brief Empty class tag for push() function + * @brief Empty class tag for push() function. */ class Object { }; /** * @class Array - * @brief Empty class tag for push() function + * @brief Empty class tag for push() function. */ class Array { }; /** * @class Global - * @brief Empty class tag to push the global object + * @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 Function - * @brief Duktape/C function definition + * @brief Duktape/C function definition. */ class Function { public: @@ -92,7 +106,7 @@ /** * @class ErrorInfo - * @brief Error description + * @brief Error description. * * This class fills the fields got in an Error object. */ @@ -117,7 +131,24 @@ /** * @class TypeInfo - * @brief Type information to implement new types in JavaScript's context + * @brief Type information to implement new types in JavaScript's context. + * + * This class depending on your needs may have the following functions: + * + * - static void push(Context &ctx, Type value) + * - static T get(Context &ctx, int index) + * - static bool is(Context &ctx, int index) + * - static void construct(Context &ctx, Type value) + * + * 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 `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 `construct` function is used in Context::construct to build a new value as this (e.g. constructors). */ template <typename Type> class TypeInfo { @@ -125,7 +156,9 @@ /** * @class File - * @brief Evaluate script from file + * @brief Evaluate script from file. + * @see Context::eval + * @see Context::peval */ class File { public: @@ -144,7 +177,9 @@ /** * @class Script - * @brief Evaluate script from raw text + * @brief Evaluate script from raw text. + * @see Context::eval + * @see Context::peval */ class Script { public: @@ -163,7 +198,7 @@ /** * @class Context - * @brief RAII based Duktape handler + * @brief RAII based Duktape handler. * * This class is implicitly convertible to duk_context for convenience. */ @@ -220,7 +255,7 @@ } /* ---------------------------------------------------------- - * Push / Get / Require + * Push / Get / Require / Is * ---------------------------------------------------------- */ /** @@ -258,6 +293,20 @@ 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 + */ + template <typename T> + inline bool is(int index) + { + return TypeInfo<T>::is(*this, index); + } + // TODO: add optional /* -------------------------------------------------------- @@ -276,7 +325,7 @@ inline auto getProperty(int index, const std::string &name, std::enable_if_t<!std::is_void<Type>::value> * = nullptr) -> decltype(get<Type>(0)) { duk_get_prop_string(m_handle.get(), index, name.c_str()); - auto &&value = get<Type>(-1); + decltype(get<Type>(0)) value = get<Type>(-1); duk_pop(m_handle.get()); return value; @@ -316,6 +365,18 @@ duk_put_prop_string(m_handle.get(), index, name.c_str()); } + /** + * 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()); + } + + /* -------------------------------------------------------- * Basic functions * -------------------------------------------------------- */ @@ -476,6 +537,53 @@ 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 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()); + } + + /** + * 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)); + } + /* -------------------------------------------------------- * Global functions * -------------------------------------------------------- */ @@ -491,7 +599,7 @@ 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()); - auto &&value = get<Type>(-1); + decltype(get<Type>(0)) value = get<Type>(-1); duk_pop(m_handle.get()); return value; @@ -514,57 +622,20 @@ * @param type the value to set */ template <typename Type> - inline void setGlobal(const std::string &name, Type&& 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()); } /** - * Throw an ECMAScript exception. + * Put the value at the top of the stack as global property. * - * @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()); - } - - /** - * Return the this binding of the current function. - * - * @return the this binding as the template given + * @param name the property name */ - template <typename T> - inline auto self() -> decltype(TypeInfo<T>::get(*this, 0)) + inline void putGlobal(const std::string &name) { - duk_push_this(m_handle.get()); - auto &&value = TypeInfo<T>::get(*this, -1); - duk_pop(m_handle.get()); - - 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)); + duk_put_global_string(m_handle.get(), name.c_str()); } }; @@ -574,7 +645,7 @@ /** * @class ExceptionAbstract - * @brief Base class for standard ECMAScript exceptions + * @brief Base class for standard ECMAScript exceptions. * @warning Override the function create for your own exceptions */ class ExceptionAbstract { @@ -621,7 +692,7 @@ /** * @class Error - * @brief Base ECMAScript error class + * @brief Base ECMAScript error class. */ class Error : public ExceptionAbstract { public: @@ -633,7 +704,7 @@ /** * @class EvalError - * @brief Error in eval() function + * @brief Error in eval() function. */ class EvalError : public ExceptionAbstract { public: @@ -645,7 +716,7 @@ /** * @class RangeError - * @brief Value is out of range + * @brief Value is out of range. */ class RangeError : public ExceptionAbstract { public: @@ -657,7 +728,7 @@ /** * @class ReferenceError - * @brief Trying to use a variable that does not exist + * @brief Trying to use a variable that does not exist. */ class ReferenceError : public ExceptionAbstract { public: @@ -669,7 +740,7 @@ /** * @class SyntaxError - * @brief Syntax error in the script + * @brief Syntax error in the script. */ class SyntaxError : public ExceptionAbstract { public: @@ -681,7 +752,7 @@ /** * @class TypeError - * @brief Invalid type given + * @brief Invalid type given. */ class TypeError : public ExceptionAbstract { public: @@ -693,7 +764,7 @@ /** * @class URIError - * @brief URI manipulation failure + * @brief URI manipulation failure. */ class URIError : public ExceptionAbstract { public: @@ -709,19 +780,24 @@ /** * @class TypeInfo<int> - * @brief Default implementation for int + * @brief Default implementation for int. * - * Provides: get, push, require, optional + * Provides: is, get, push. */ template <> class TypeInfo<int> { public: - static int get(ContextPtr ctx, int index) + static inline bool is(Context &ctx, int index) + { + return duk_is_number(ctx, index); + } + + static inline int get(Context &ctx, int index) { return duk_get_int(ctx, index); } - static void push(ContextPtr ctx, int value) + static inline void push(Context &ctx, int value) { duk_push_int(ctx, value); } @@ -729,19 +805,24 @@ /** * @class TypeInfo<bool> - * @brief Default implementation for bool + * @brief Default implementation for bool. * - * Provides: get, push, require, optional + * Provides: is, get, push */ template <> class TypeInfo<bool> { public: - static bool get(Context &ctx, int index) + static inline bool is(Context &ctx, int index) + { + return duk_is_boolean(ctx, index); + } + + static inline bool get(Context &ctx, int index) { return duk_get_boolean(ctx, index); } - static void push(Context &ctx, bool value) + static inline void push(Context &ctx, bool value) { duk_push_boolean(ctx, value); } @@ -749,19 +830,24 @@ /** * @class TypeInfo<double> - * @brief Default implementation for double + * @brief Default implementation for double. * - * Provides: get, push, require, optional + * Provides: is, get, push */ template <> class TypeInfo<double> { public: - static double get(Context &ctx, int index) + static inline bool is(Context &ctx, int index) + { + return duk_is_number(ctx, index); + } + + static inline double get(Context &ctx, int index) { return duk_get_number(ctx, index); } - static void push(Context &ctx, double value) + static inline void push(Context &ctx, double value) { duk_push_number(ctx, value); } @@ -769,16 +855,21 @@ /** * @class TypeInfo<std::string> - * @brief Default implementation for std::string + * @brief Default implementation for std::string. * - * Provides: get, push, require, optional + * Provides: is, get, push. * * Note: the functions allows embedded '\0'. */ template <> class TypeInfo<std::string> { public: - static std::string get(Context &ctx, int index) + static inline bool is(Context &ctx, int index) + { + return duk_is_string(ctx, index); + } + + static inline std::string get(Context &ctx, int index) { duk_size_t size; const char *text = duk_get_lstring(ctx, index, &size); @@ -786,7 +877,7 @@ return std::string{text, size}; } - static void push(Context &ctx, const std::string &value) + static inline void push(Context &ctx, const std::string &value) { duk_push_lstring(ctx, value.c_str(), value.length()); } @@ -794,14 +885,24 @@ /** * @class TypeInfo<const char *> - * @brief Default implementation for const char literals + * @brief Default implementation for const char literals. * - * Provides: push + * Provides: is, get, push */ template <> class TypeInfo<const char *> { public: - static void push(Context &ctx, const char *value) + static inline bool is(Context &ctx, int index) + { + return duk_is_string(ctx, index); + } + + static inline const char *get(Context &ctx, int index) + { + return duk_get_string(ctx, index); + } + + static inline void push(Context &ctx, const char *value) { duk_push_string(ctx, value); } @@ -809,7 +910,7 @@ /** * @class TypeInfo<Function> - * @brief Default implementation for Function + * @brief Push C++ function to the stack. * * Provides: push * @@ -862,10 +963,16 @@ } }; +/** + * @class TypeInfo<FunctionMap> + * @brief Put the functions to the object at the top of the stack. + * + * Provides: push + */ template <> class TypeInfo<FunctionMap> { public: - static void push(Context &ctx, const FunctionMap &map) + static inline void push(Context &ctx, const FunctionMap &map) { for (const auto &entry : map) ctx.putProperty(-1, entry.first, entry.second); @@ -874,16 +981,19 @@ /** * @class TypeInfo<Object> - * @brief Default implementation for Object + * @brief Push empty object to the stack. * - * Provides: push - * - * This implementation is provided to push empty object into the stack. + * Provides: is, push */ template <> class TypeInfo<Object> { public: - static void push(Context &ctx, const Object &) + static inline bool is(Context &ctx, int index) + { + return duk_is_object(ctx, index); + } + + static inline void push(Context &ctx, const Object &) { duk_push_object(ctx); } @@ -891,25 +1001,74 @@ /** * @class TypeInfo<Array> - * @brief Default implementation for Array + * @brief Push empty array to the stack. * - * Provides: push - * - * This implementation is provided to push empty array into the stack. + * Provides: is, push */ template <> class TypeInfo<Array> { public: - static void push(Context &ctx, const Array &) + static inline bool is(Context &ctx, int index) + { + return duk_is_array(ctx, index); + } + + 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: + static inline bool is(Context &ctx, int index) + { + return duk_is_undefined(ctx, index); + } + + 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: + static inline bool is(Context &ctx, int index) + { + return duk_is_null(ctx, index); + } + + static inline void push(Context &ctx, const Null &) + { + duk_push_null(ctx); + } +}; + +/** + * @class TypeInfo<Global> + * @brief Push the global object to the stack. + * + * Provides: push + */ template <> class TypeInfo<Global> { public: - static void push(Context &ctx, const Global &) + static inline void push(Context &ctx, const Global &) { duk_push_global_object(ctx); } @@ -921,15 +1080,9 @@ /** * @class TypeInfoShared - * @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(), require() and optional() functions. + * @brief Push objects as shared_ptr. * - * 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. + * TODO: add better documentation when finalized. */ template <typename T> class TypeInfoShared { @@ -1001,16 +1154,9 @@ /** * @class TypeInfoPointer - * @brief Generates push / get / require / optional / construct for custom pointer object - * - * Specialize TypeInfo<T *> and inherits from this class to implement the push(), - * get(), require() and optional() functions. + * @brief Push pointers as objects. * - * 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 pointers objects and is useful when you want to push C++ - * objects that will be only needed into JavaScript side. + * TODO: add better documentation when finalized. */ template <typename T> class TypeInfoPointer {
--- a/C++/tests/Js/main.cpp Thu Oct 01 14:02:28 2015 +0200 +++ b/C++/tests/Js/main.cpp Thu Oct 01 21:30:57 2015 +0200 @@ -64,6 +64,98 @@ ASSERT_EQ("hello world!", context.get<std::string>(-1)); } +TEST(PushAndGet, undefined) +{ + Context context; + + context.push(Undefined{}); + ASSERT_EQ(DUK_TYPE_UNDEFINED, context.type(-1)); +} + +TEST(PushAndGet, null) +{ + Context context; + + context.push(Null{}); + ASSERT_EQ(DUK_TYPE_NULL, context.type(-1)); +} + +/* -------------------------------------------------------- + * Is + * -------------------------------------------------------- */ + +TEST(Is, boolean) +{ + Context context; + + context.push(true); + ASSERT_TRUE(context.is<bool>(-1)); +} + +TEST(Is, integer) +{ + Context context; + + context.push(123); + ASSERT_TRUE(context.is<int>(-1)); +} + +TEST(Is, number) +{ + Context context; + + context.push(50.5); + ASSERT_TRUE(context.is<double>(-1)); +} + +TEST(Is, string) +{ + Context context; + + context.push(std::string{"hello"}); + ASSERT_TRUE(context.is<std::string>(-1)); +} + +TEST(Is, cstring) +{ + Context context; + + context.push("hello"); + ASSERT_TRUE(context.is<const char *>(-1)); +} + +TEST(Is, undefined) +{ + Context context; + + context.push(Undefined{}); + ASSERT_TRUE(context.is<Undefined>(-1)); +} + +TEST(Is, null) +{ + Context context; + + context.push(Null{}); + ASSERT_TRUE(context.is<Null>(-1)); +} + +TEST(Is, object) +{ + Context context; + + context.push(Object{}); + ASSERT_TRUE(context.is<Object>(-1)); +} + +TEST(Is, array) +{ + Context context; + + context.push(Array{}); + ASSERT_TRUE(context.is<Array>(-1)); +} + /* -------------------------------------------------------- * Basics * -------------------------------------------------------- */