Mercurial > code
view C++/modules/Js/Js.h @ 391:e2cefd0ee511
Add Js, Duktape wrapper
author | David Demelier <markand@malikania.fr> |
---|---|
date | Mon, 28 Sep 2015 13:18:14 +0200 |
parents | |
children | 69adcefe73ae |
line wrap: on
line source
/* * Js.h -- JavaScript wrapper for Duktape * * Copyright (c) 2013, 2014, 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 <cassert> #include <cerrno> #include <cstring> #include <memory> #include <stack> #include <string> #include <vector> #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 /** * Typedef for readability. */ 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 */ class Object { }; /** * @class Arary * @brief Empty class tag for push() function */ class Array { }; /** * @class Function * @brief Duktape/C function definition */ class Function { public: /** * The function pointer, must not be null. */ duk_c_function function{nullptr}; /** * Number of args that the function takes */ duk_idx_t nargs{0}; }; /** * @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(); } }; /* -------------------------------------------------------- * 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); /** * 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. * * @param ctx the context * @param nargs the number of arguments * @throw ErrorInfo on errors */ void pcall(ContextPtr ctx, duk_idx_t nargs = 0); /* ------------------------------------------------------------------ * Eval functions * ------------------------------------------------------------------ */ /** * Evaluate a non-protected chunk at the top of the stack. * * @param ctx */ void eval(ContextPtr ctx); /** * Evaluate a non-protected string script. * * @param ctx the context * @param script the script content */ void evalString(ContextPtr ctx, const std::string &script); /** * Evaluate a non-protected file. * * @param ctx the context * @param file the file */ void evalFile(ContextPtr ctx, const std::string &file); /** * Evaluate a protected chunk. * * @param ctx the context * @throw ErrorInfo the error */ void peval(ContextPtr ctx); /** * 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); /** * Evaluate a protected file. * * @param ctx the context * @param file the file * @throw ErrorInfo the error */ void pevalFile(ContextPtr ctx, const std::string &file); /* ------------------------------------------------------------------ * Extended functions * ------------------------------------------------------------------ */ /** * 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); while (duk_next(ctx, -1, getvalue)) { func(ctx); duk_pop_n(ctx, 1 + (getvalue ? 1 : 0)); } duk_pop(ctx); assertEquals(ctx); } /* -------------------------------------------------------- * Global 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); 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(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); } /* ------------------------------------------------------------------ * Exception handling * ------------------------------------------------------------------ */ /** * @class ExceptionAbstract * @brief Base class for standard ECMAScript exceptions * @warning Override the function create for your own exceptions */ class ExceptionAbstract { protected: std::string m_name; std::string m_message; public: /** * Construct an exception of type name with the specified message. * * @param name the name (e.g TypeError) * @param message the message */ ExceptionAbstract(std::string name, std::string message); /** * Get the exception type name. * * @return the exception type */ const std::string &name() const noexcept; /** * 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; }; /** * @class Error * @brief Base ECMAScript error class */ class Error : public ExceptionAbstract { public: Error(std::string message); }; /** * @class EvalError * @brief Error in eval() function */ class EvalError : public ExceptionAbstract { public: EvalError(std::string message); }; /** * @class RangeError * @brief Value is out of range */ class RangeError : public ExceptionAbstract { public: RangeError(std::string message); }; /** * @class ReferenceError * @brief Trying to use a variable that does not exist */ class ReferenceError : public ExceptionAbstract { public: ReferenceError(std::string message); }; /** * @class SyntaxError * @brief Syntax error in the script */ class SyntaxError : public ExceptionAbstract { public: SyntaxError(std::string message); }; /** * @class TypeError * @brief Invalid type given */ class TypeError : public ExceptionAbstract { public: TypeError(std::string message); }; /** * @class URIError * @brief URI manipulation failure */ class URIError : public ExceptionAbstract { public: 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); duk_push_string(ctx, ex.name().c_str()); duk_put_prop_string(ctx, -2, "name"); duk_throw(ctx); } } // !js #endif // !_JS_H_