Mercurial > malikania
changeset 64:f8cb71805a4b
Misc: move duktape.hpp into libcommon-js
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 16 Dec 2016 16:13:26 +0100 |
parents | 96ba0c5cf893 |
children | 30ea265decdb |
files | libcommon-js/CMakeLists.txt libcommon-js/malikania/duktape.hpp libcommon/CMakeLists.txt libcommon/malikania/duktape.hpp |
diffstat | 4 files changed, 343 insertions(+), 343 deletions(-) [+] |
line wrap: on
line diff
--- a/libcommon-js/CMakeLists.txt Fri Dec 16 16:11:24 2016 +0100 +++ b/libcommon-js/CMakeLists.txt Fri Dec 16 16:13:26 2016 +0100 @@ -20,6 +20,7 @@ set( HEADERS + ${libcommon-js_SOURCE_DIR}/malikania/duktape.hpp ${libcommon-js_SOURCE_DIR}/malikania/js_elapsed_timer.hpp ${libcommon-js_SOURCE_DIR}/malikania/js_resources_loader.hpp )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libcommon-js/malikania/duktape.hpp Fri Dec 16 16:13:26 2016 +0100 @@ -0,0 +1,342 @@ +/* + * duktape.hpp -- Duktape extras + * + * Copyright (c) 2016 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 MALIKANIA_COMMON_DUKTAPE_HPP +#define MALIKANIA_COMMON_DUKTAPE_HPP + +/** + * \file duktape.hpp + * \brief Bring some extras to Duktape C library. + * \author David Demelier <markand@malikania.fr> + */ + +#include <cstdio> +#include <cstdlib> +#include <memory> +#include <string> +#include <unordered_map> +#include <utility> +#include <vector> + +#include <duktape.h> + +/** + * \brief Stack sanity checker. + * + * Instanciate this class where you need to manipulate the Duktape stack outside + * a Duktape/C function, its destructor will examinate if the stack size matches + * the user expected size. + * + * When compiled with NDEBUG, this class does nothing. + * + * To use it, just declare an lvalue at the beginning of your function. + */ +class dukx_stack_assert { +#if !defined(NDEBUG) +private: + duk_context* m_context; + unsigned m_expected; + int m_at_start; +#endif + +public: + /** + * Create the stack checker. + * + * No-op if NDEBUG is set. + * + * \param ctx the context + * \param expected the size expected relative to the already existing values + */ + inline dukx_stack_assert(duk_context *ctx, unsigned expected = 0) noexcept +#if !defined(NDEBUG) + : m_context(ctx) + , m_expected(expected) + , m_at_start(duk_get_top(ctx)) +#endif + { +#if defined(NDEBUG) + (void)ctx; + (void)expected; +#endif + } + + /** + * Verify the expected size. + * + * No-op if NDEBUG is set. + */ + inline ~dukx_stack_assert() noexcept + { +#if !defined(NDEBUG) + auto result = duk_get_top(m_context) - m_at_start; + + if (result != static_cast<int>(m_expected)) { + std::fprintf(stderr, "Corrupt stack detection in dukx_stack_assert:\n"); + std::fprintf(stderr, " Size at start: %d\n", m_at_start); + std::fprintf(stderr, " Size at end: %d\n", duk_get_top(m_context)); + std::fprintf(stderr, " Expected (user): %u\n", m_expected); + std::fprintf(stderr, " Expected (adjusted): %u\n", m_expected + m_at_start); + std::fprintf(stderr, " Difference count: %+d\n", result - m_expected); + std::abort(); + } +#endif + } +}; + +/** + * \brief Error description. + * + * This class fills the fields got in an Error object. + */ +class dukx_exception : 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(); + } +}; + +/** + * \brief RAII based Duktape handler. + * + * This class is implicitly convertible to duk_context for convenience. + */ +class dukx_context { +private: + std::unique_ptr<duk_context, void (*)(duk_context*)> m_handle; + + dukx_context(const dukx_context&) = delete; + dukx_context &operator=(const dukx_context&) = delete; + +public: + /** + * Create default context. + */ + inline dukx_context() noexcept + : m_handle(duk_create_heap_default(), duk_destroy_heap) + { + } + + /** + * Default move constructor. + */ + dukx_context(dukx_context&&) noexcept = default; + + /** + * Convert the context to the native Duktape/C type. + * + * \return the duk_context + */ + inline operator duk_context*() noexcept + { + return m_handle.get(); + } + + /** + * Convert the context to the native Duktape/C type. + * + * \return the duk_context + */ + inline operator duk_context*() const noexcept + { + return m_handle.get(); + } + + /** + * Default move assignment operator. + * + * \return this + */ + dukx_context& operator=(dukx_context&&) noexcept = delete; +}; + +/** + * Get the error object when a JavaScript error has been thrown (e.g. eval + * failure). + * + * \param ctx the context + * \param index the index + * \param pop if true, also remove the exception from the stack + * \return the information + */ +inline dukx_exception dukx_get_exception(duk_context* ctx, int index, bool pop = true) +{ + dukx_exception ex; + + index = duk_normalize_index(ctx, index); + + duk_get_prop_string(ctx, index, "name"); + ex.name = duk_to_string(ctx, -1); + duk_get_prop_string(ctx, index, "message"); + ex.message = duk_to_string(ctx, -1); + duk_get_prop_string(ctx, index, "fileName"); + ex.fileName = duk_to_string(ctx, -1); + duk_get_prop_string(ctx, index, "lineNumber"); + ex.lineNumber = duk_to_int(ctx, -1); + duk_get_prop_string(ctx, index, "stack"); + ex.stack = duk_to_string(ctx, -1); + duk_pop_n(ctx, 5); + + if (pop) { + duk_remove(ctx, index); + } + + return ex; +} + +/** + * Get a string, return 0 if not a string. + * + * \param ctx the context + * \param index the index + * \return the string + */ +inline std::string dukx_get_std_string(duk_context* ctx, int index) +{ + duk_size_t size; + const char* text = duk_get_lstring(ctx, index, &size); + + return std::string(text, size); +} + +/** + * Require a string, throws a JavaScript exception if not a string. + * + * \param ctx the context + * \param index the index + * \return the string + */ +inline std::string dukx_require_std_string(duk_context* ctx, int index) +{ + duk_size_t size; + const char* text = duk_require_lstring(ctx, index, &size); + + return std::string(text, size); +} + +/** + * Push a C++ string. + * + * \param ctx the context + * \param str the string + */ +inline void dukx_push_std_string(duk_context* ctx, const std::string& str) +{ + duk_push_lstring(ctx, str.data(), str.length()); +} + +/** + * Get an array. + * + * \param ctx the context + * \param index the array index + * \param get the conversion function (e.g. duk_get_int) + */ +template <typename Getter> +auto dukx_get_array(duk_context* ctx, duk_idx_t index, Getter&& get) +{ + using T = decltype(get(ctx, 0)); + + std::vector<T> result; + std::size_t length = duk_get_length(ctx, index); + + for (std::size_t i = 0; i < length; ++i) { + duk_get_prop_index(ctx, -1, i); + result.push_back(get(ctx, -1)); + duk_pop(ctx); + } + + return result; +} + +/** + * Push an array. + * + * \param ctx the context + * \param values the values + * \param push the function to push values + */ +template <typename T, typename Pusher> +void dukx_push_array(duk_context* ctx, const std::vector<T>& values, Pusher&& push) +{ + duk_push_array(ctx); + + int i = 0; + for (auto x : values) { + push(ctx, x); + duk_put_prop_index(ctx, -2, i++); + } +} + +/** + * Get an object. + * + * \param ctx the context + * \param index the object index + * \param get the conversion function (e.g. duk_get_int) + */ +template <typename Getter> +auto dukx_get_object(duk_context* ctx, duk_idx_t index, Getter&& get) +{ + using T = decltype(get(ctx, 0)); + + std::unordered_map<std::string, T> result; + + duk_enum(ctx, index, 0); + + while (duk_next(ctx, -1, true)) { + result.emplace(dukx_get_std_string(ctx, -2), get(ctx, -1)); + duk_pop_2(ctx); + } + + duk_pop(ctx); + + return result; +} + +/** + * Push an object. + * + * \param ctx the context + * \param values the values + * \param push the function to push values + */ +template <typename T, typename Pusher> +void dukx_push_object(duk_context* ctx, const std::unordered_map<std::string, T>& values, Pusher&& push) +{ + duk_push_object(ctx); + + for (const auto& pair : values) { + push(ctx, pair.second); + duk_put_prop_string(ctx, -2, pair.first.c_str()); + } +} + +#endif // !MALIKANIA_COMMON_DUKTAPE_HPP
--- a/libcommon/CMakeLists.txt Fri Dec 16 16:11:24 2016 +0100 +++ b/libcommon/CMakeLists.txt Fri Dec 16 16:13:26 2016 +0100 @@ -20,7 +20,6 @@ set( HEADERS - ${libcommon_SOURCE_DIR}/malikania/duktape.hpp ${libcommon_SOURCE_DIR}/malikania/game.hpp ${libcommon_SOURCE_DIR}/malikania/id.hpp ${libcommon_SOURCE_DIR}/malikania/resources_loader.hpp
--- a/libcommon/malikania/duktape.hpp Fri Dec 16 16:11:24 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,342 +0,0 @@ -/* - * duktape.hpp -- Duktape extras - * - * Copyright (c) 2016 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 MALIKANIA_COMMON_DUKTAPE_HPP -#define MALIKANIA_COMMON_DUKTAPE_HPP - -/** - * \file duktape.hpp - * \brief Bring some extras to Duktape C library. - * \author David Demelier <markand@malikania.fr> - */ - -#include <cstdio> -#include <cstdlib> -#include <memory> -#include <string> -#include <unordered_map> -#include <utility> -#include <vector> - -#include <duktape.h> - -/** - * \brief Stack sanity checker. - * - * Instanciate this class where you need to manipulate the Duktape stack outside - * a Duktape/C function, its destructor will examinate if the stack size matches - * the user expected size. - * - * When compiled with NDEBUG, this class does nothing. - * - * To use it, just declare an lvalue at the beginning of your function. - */ -class dukx_stack_assert { -#if !defined(NDEBUG) -private: - duk_context* m_context; - unsigned m_expected; - int m_at_start; -#endif - -public: - /** - * Create the stack checker. - * - * No-op if NDEBUG is set. - * - * \param ctx the context - * \param expected the size expected relative to the already existing values - */ - inline dukx_stack_assert(duk_context *ctx, unsigned expected = 0) noexcept -#if !defined(NDEBUG) - : m_context(ctx) - , m_expected(expected) - , m_at_start(duk_get_top(ctx)) -#endif - { -#if defined(NDEBUG) - (void)ctx; - (void)expected; -#endif - } - - /** - * Verify the expected size. - * - * No-op if NDEBUG is set. - */ - inline ~dukx_stack_assert() noexcept - { -#if !defined(NDEBUG) - auto result = duk_get_top(m_context) - m_at_start; - - if (result != static_cast<int>(m_expected)) { - std::fprintf(stderr, "Corrupt stack detection in dukx_stack_assert:\n"); - std::fprintf(stderr, " Size at start: %d\n", m_at_start); - std::fprintf(stderr, " Size at end: %d\n", duk_get_top(m_context)); - std::fprintf(stderr, " Expected (user): %u\n", m_expected); - std::fprintf(stderr, " Expected (adjusted): %u\n", m_expected + m_at_start); - std::fprintf(stderr, " Difference count: %+d\n", result - m_expected); - std::abort(); - } -#endif - } -}; - -/** - * \brief Error description. - * - * This class fills the fields got in an Error object. - */ -class dukx_exception : 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(); - } -}; - -/** - * \brief RAII based Duktape handler. - * - * This class is implicitly convertible to duk_context for convenience. - */ -class dukx_context { -private: - std::unique_ptr<duk_context, void (*)(duk_context*)> m_handle; - - dukx_context(const dukx_context&) = delete; - dukx_context &operator=(const dukx_context&) = delete; - -public: - /** - * Create default context. - */ - inline dukx_context() noexcept - : m_handle(duk_create_heap_default(), duk_destroy_heap) - { - } - - /** - * Default move constructor. - */ - dukx_context(dukx_context&&) noexcept = default; - - /** - * Convert the context to the native Duktape/C type. - * - * \return the duk_context - */ - inline operator duk_context*() noexcept - { - return m_handle.get(); - } - - /** - * Convert the context to the native Duktape/C type. - * - * \return the duk_context - */ - inline operator duk_context*() const noexcept - { - return m_handle.get(); - } - - /** - * Default move assignment operator. - * - * \return this - */ - dukx_context& operator=(dukx_context&&) noexcept = delete; -}; - -/** - * Get the error object when a JavaScript error has been thrown (e.g. eval - * failure). - * - * \param ctx the context - * \param index the index - * \param pop if true, also remove the exception from the stack - * \return the information - */ -inline dukx_exception dukx_get_exception(duk_context* ctx, int index, bool pop = true) -{ - dukx_exception ex; - - index = duk_normalize_index(ctx, index); - - duk_get_prop_string(ctx, index, "name"); - ex.name = duk_to_string(ctx, -1); - duk_get_prop_string(ctx, index, "message"); - ex.message = duk_to_string(ctx, -1); - duk_get_prop_string(ctx, index, "fileName"); - ex.fileName = duk_to_string(ctx, -1); - duk_get_prop_string(ctx, index, "lineNumber"); - ex.lineNumber = duk_to_int(ctx, -1); - duk_get_prop_string(ctx, index, "stack"); - ex.stack = duk_to_string(ctx, -1); - duk_pop_n(ctx, 5); - - if (pop) { - duk_remove(ctx, index); - } - - return ex; -} - -/** - * Get a string, return 0 if not a string. - * - * \param ctx the context - * \param index the index - * \return the string - */ -inline std::string dukx_get_std_string(duk_context* ctx, int index) -{ - duk_size_t size; - const char* text = duk_get_lstring(ctx, index, &size); - - return std::string(text, size); -} - -/** - * Require a string, throws a JavaScript exception if not a string. - * - * \param ctx the context - * \param index the index - * \return the string - */ -inline std::string dukx_require_std_string(duk_context* ctx, int index) -{ - duk_size_t size; - const char* text = duk_require_lstring(ctx, index, &size); - - return std::string(text, size); -} - -/** - * Push a C++ string. - * - * \param ctx the context - * \param str the string - */ -inline void dukx_push_std_string(duk_context* ctx, const std::string& str) -{ - duk_push_lstring(ctx, str.data(), str.length()); -} - -/** - * Get an array. - * - * \param ctx the context - * \param index the array index - * \param get the conversion function (e.g. duk_get_int) - */ -template <typename Getter> -auto dukx_get_array(duk_context* ctx, duk_idx_t index, Getter&& get) -{ - using T = decltype(get(ctx, 0)); - - std::vector<T> result; - std::size_t length = duk_get_length(ctx, index); - - for (std::size_t i = 0; i < length; ++i) { - duk_get_prop_index(ctx, -1, i); - result.push_back(get(ctx, -1)); - duk_pop(ctx); - } - - return result; -} - -/** - * Push an array. - * - * \param ctx the context - * \param values the values - * \param push the function to push values - */ -template <typename T, typename Pusher> -void dukx_push_array(duk_context* ctx, const std::vector<T>& values, Pusher&& push) -{ - duk_push_array(ctx); - - int i = 0; - for (auto x : values) { - push(ctx, x); - duk_put_prop_index(ctx, -2, i++); - } -} - -/** - * Get an object. - * - * \param ctx the context - * \param index the object index - * \param get the conversion function (e.g. duk_get_int) - */ -template <typename Getter> -auto dukx_get_object(duk_context* ctx, duk_idx_t index, Getter&& get) -{ - using T = decltype(get(ctx, 0)); - - std::unordered_map<std::string, T> result; - - duk_enum(ctx, index, 0); - - while (duk_next(ctx, -1, true)) { - result.emplace(dukx_get_std_string(ctx, -2), get(ctx, -1)); - duk_pop_2(ctx); - } - - duk_pop(ctx); - - return result; -} - -/** - * Push an object. - * - * \param ctx the context - * \param values the values - * \param push the function to push values - */ -template <typename T, typename Pusher> -void dukx_push_object(duk_context* ctx, const std::unordered_map<std::string, T>& values, Pusher&& push) -{ - duk_push_object(ctx); - - for (const auto& pair : values) { - push(ctx, pair.second); - duk_put_prop_string(ctx, -2, pair.first.c_str()); - } -} - -#endif // !MALIKANIA_COMMON_DUKTAPE_HPP