From 250d6a4dbe61b6798cd090abeabdc0ece8237dd3 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Sat, 25 Oct 2014 21:23:38 +0000 Subject: some restructuring, added first version of the mozjs plugin git-svn-id: file:///var/local/svn/basicwriter@78 daaaf23c-2e50-4459-9457-1e69db5a47bf --- src/core/model/RangeSet.hpp | 326 -------------------------------- src/core/script/Object.hpp | 2 + src/core/script/ScriptEngine.cpp | 32 ++-- src/core/script/ScriptEngine.hpp | 46 ++--- src/core/utils/RangeSet.hpp | 326 ++++++++++++++++++++++++++++++++ src/plugins/mozjs/MozJsScriptEngine.cpp | 218 +++++++++++++++++++++ src/plugins/mozjs/MozJsScriptEngine.hpp | 76 ++++++++ 7 files changed, 653 insertions(+), 373 deletions(-) delete mode 100644 src/core/model/RangeSet.hpp create mode 100644 src/core/utils/RangeSet.hpp create mode 100644 src/plugins/mozjs/MozJsScriptEngine.cpp create mode 100644 src/plugins/mozjs/MozJsScriptEngine.hpp (limited to 'src') diff --git a/src/core/model/RangeSet.hpp b/src/core/model/RangeSet.hpp deleted file mode 100644 index 841d476..0000000 --- a/src/core/model/RangeSet.hpp +++ /dev/null @@ -1,326 +0,0 @@ -/* - Ousía - Copyright (C) 2014 Benjamin Paaßen, Andreas Stöckel - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef _OUSIA_MODEL_RANGE_SET_HPP_ -#define _OUSIA_MODEL_RANGE_SET_HPP_ - -#include -#include - -namespace ousia { -namespace model { -/** - * The Range structure represents an interval of numerical values of type T. - */ -template -struct Range { - /** - * Start is the start value of the range. - */ - T start; - - /** - * End is the end value of the range (inclusively). - */ - T end; - - /** - * Default constructor of the range class. The range is initialized as - * invalid, with start being set to the maximum possible value of the - * numerical type T, and end being set to the minimum possible value. - */ - Range() : - start(std::numeric_limits::max()), - end(std::numeric_limits::min()) - { - // Do nothing here - } - - /** - * Copies the given start and end value. The given values are not checked - * for validity. Use the "isValid" - * - * @param start is the minimum value the range still covers. - * @param end is the maximum value the range still covers. - */ - Range(const T &start, const T &end) : - start(start), end(end) - { - // Do nothing here - } - - /** - * Creates a range that covers exactly one element, namely the value given - * as parameter n. - */ - Range(const T &n) : - start(n), end(n) - { - // Do nothing here - } - - /** - * Returns true if this range is valid, e.g. its start value is smaller or - * equal to its end value. - * - * @return true if start is smaller or equal to end, false otherwise. - */ - bool isValid() const - { - return start <= end; - } - - /** - * Checks whether the given value lies inside the range. - * - * @param v is the value that is being checked. - * @return true if the value lies within the range, false otherwise. - */ - bool inRange(T v) const - { - return (v >= start) && (v <= end); - } - - /** - * Checks whether the given range overlaps with another range. Not that - * this check is only meaningful if both ranges are valid. - * - * @param r is the range that should be checked for overlapping with this - * range. - */ - bool overlaps(const Range &r) const - { - return (((r.start >= start) || (r.end >= start)) - && ((r.start <= end) || (r.end <= end))); - } - - /** - * Returns true if the two given ranges are neighbours (their limits only - * differ in the smallest representable difference between them). - */ - bool neighbours(const Range &r) const - { - constexpr T eps = std::numeric_limits::is_integer - ? 1 : std::numeric_limits::epsilon(); - return ((r.start > end) && ((r.start - eps) <= end)) - || ((r.end < start) && ((r.end + eps) >= start)); - } - - /** - * Checks whether the given range completely covers this range. - */ - bool coveredBy(const Range &r) const - { - return (r.start <= start) && (r.end >= end); - } - - /** - * Checks whether this range completely covers the given range. - */ - bool covers(const Range &r) const - { - return r.coveredBy(*this); - } - - /** - * Calculates the union of the two ranges -- not that this operation is only - * valid if the ranges overlapp. Use the RangeSet class if you cannot - * guarantee that. - */ - Range merge(const Range &r) const - { - return Range(std::min(start, r.start), std::max(end, r.end)); - } - - /** - * Returns a range that represents the spans the complete set defined by the - * given type T. - */ - static Range typeRange() - { - return Range(std::numeric_limits::min(), - std::numeric_limits::max()); - } - - /** - * Returns a range that represents the spans the complete set defined by the - * given type T up to a given value. - * - * @param till is the value up to which the range should be defined (till is - * included in the set). - */ - static Range typeRangeUntil(const T &till) - { - return Range(std::numeric_limits::min(), till); - } - - /** - * Returns a range that represents the spans the complete set defined by the - * given type T up to a given value. - * - * @param from is the value from which the range should be defined (from is - * included in the set). - */ - static Range typeRangeFrom(const T &from) - { - return Range(from, std::numeric_limits::max()); - } -}; - -/** - * RangeComp is a comperator used to order to sort the ranges within the - * ranges list. Sorts by the start element. - */ -template -struct RangeComp { - bool operator() (const Range& lhs, const Range& rhs) const - { - return lhs.start < rhs.start; - } -}; - -/** - * RangeSet represents a set of ranges of the given numerical type and is thus - * capable of representing any possible subset of the given numerical type T. - */ -template -class RangeSet { - -protected: - /** - * Set of ranges used internally. - */ - std::set, RangeComp> ranges; - - /** - * Returns an iterator to the first element in the ranges list that overlaps - * with the given range. - * - * @param r is the range for which the first overlapping element should be - * found. - * @return an iterator pointing to the first overlapping element or to the - * end of the list if no such element was found. - */ - typename std::set, RangeComp>::iterator firstOverlapping( - const Range &r, const bool allowNeighbours) - { - // Find the element with the next larger start value compared to the - // start value given in r. - auto it = ranges.upper_bound(r); - - // Go back one element - if (it != ranges.begin()) { - it--; - } - - // Iterate until an overlapping element is found - while (!(it->overlaps(r) || (allowNeighbours && it->neighbours(r))) - && (it != ranges.end())) { - it++; - } - return it; - } - -public: - /** - * Calculates the union of this range set and the given range. - * - * @param range is the range that should be merged into this range set. - */ - void merge(Range r) - { - // Calculate a new range that covers both the new range and all old - // ranges in the set -- delete all old elements on the way - auto it = firstOverlapping(r, true); - while ((it->overlaps(r) || it->neighbours(r)) && it != ranges.end()) { - r = r.merge(*it); - it = ranges.erase(it); - } - - // Insert the new range - ranges.insert(r); - } - - /** - * Calculates the union of this range set and the given range set. - * - * @param ranges is another range set for which the union with this set - * should be calculated. - */ - void merge(const RangeSet &s) - { - for (Range &r : s.ranges) { - merge(r); - } - } - - /** - * Checks whether this range set S contains the given range R: - * S u R = R - * (The intersection between R and S equals the given range) - * - * @param r is the range for which the containment should be checked. - * @return true if the above condition is met, false otherwise. - */ - bool contains(const Range &r) - { - auto it = firstOverlapping(r, false); - if (it != ranges.end()) { - return (*it).covers(r); - } - return false; - } - - /** - * Checks whether this range set S1 contains the given range set S2: - * - * @param s is the range for which the containment should be checked. - * @return true if the above condition is met, false otherwise. - */ - bool contains(const RangeSet &s) - { - bool res = true; - for (Range &r : s.ranges) { - res = res && contains(r); - } - return res; - } - - /** - * Empties the set. - */ - void clear() - { - ranges.clear(); - } - - /** - * Returns the current list of ranges as a const reference. - */ - const std::set, RangeComp>& getRanges() - { - return this->ranges; - } - -}; - -} -} - -#endif /* _OUSIA_MODEL_RANGE_SET_HPP_ */ - diff --git a/src/core/script/Object.hpp b/src/core/script/Object.hpp index fafe632..350f800 100644 --- a/src/core/script/Object.hpp +++ b/src/core/script/Object.hpp @@ -27,6 +27,8 @@ namespace ousia { namespace script { +// TODO: Check names for being proper identifiers! + /** * The Property struct represents an object property with corresponding getter * and setter function. diff --git a/src/core/script/ScriptEngine.cpp b/src/core/script/ScriptEngine.cpp index f34ccea..99f2d3f 100644 --- a/src/core/script/ScriptEngine.cpp +++ b/src/core/script/ScriptEngine.cpp @@ -23,21 +23,27 @@ namespace ousia { namespace script { -ScriptEngineException::ScriptEngineException(int line, int col, - const std::string &msg) : - line(line), col(col), - msg(std::to_string(line) + ":" + std::to_string(col) + " " + msg) {} +/* Class ScriptEngineException */ -ScriptEngineException::ScriptEngineException(const std::string &msg) : - line(-1), col(-1), msg(msg) {} +ScriptEngineException::ScriptEngineException(int line, int col, + const std::string &msg) + : line(line), + col(col), + msg(std::to_string(line) + ":" + std::to_string(col) + " " + msg) +{ +} -const char* ScriptEngineException::what() const noexcept +ScriptEngineException::ScriptEngineException(const std::string &msg) + : line(-1), col(-1), msg(msg) { - return msg.c_str(); } +const char *ScriptEngineException::what() const noexcept { return msg.c_str(); } + +/* Class ScriptEngineFactory */ + void ScriptEngineFactory::registerScriptEngine(const std::string &name, - ScriptEngine *engine) + ScriptEngine *engine) { registry[name] = engine; } @@ -47,10 +53,8 @@ bool ScriptEngineFactory::unregisterScriptEngine(const std::string &name) return registry.erase(name) > 0; } -/* Class ScriptEngineFactory */ - -ScriptEngineScope* ScriptEngineFactory::createScope( - const std::string &name) const +ScriptEngineScope *ScriptEngineFactory::createScope( + const std::string &name) const { auto it = registry.find(name); if (it != registry.end()) { @@ -58,8 +62,6 @@ ScriptEngineScope* ScriptEngineFactory::createScope( } return nullptr; } - - } } diff --git a/src/core/script/ScriptEngine.hpp b/src/core/script/ScriptEngine.hpp index 5443460..2341beb 100644 --- a/src/core/script/ScriptEngine.hpp +++ b/src/core/script/ScriptEngine.hpp @@ -35,7 +35,6 @@ namespace script { * in the script engine. */ class ScriptEngineException : public std::exception { - public: /** * Line and column at which the exception occured. Set to -1 if the error @@ -67,18 +66,15 @@ public: /** * Returns the error message. */ - virtual const char* what() const noexcept override; - + virtual const char *what() const noexcept override; }; /** * The ScriptEngineScope class represents an execution scope -- an execution - * scope is the base class + * scope is the base class */ class ScriptEngineScope { - private: - /** * Helper used to check the given identifiers for their validity. * @@ -93,29 +89,27 @@ private: } protected: - /** - * Implementation of the @see run function. + * Implementation of the run function. */ virtual Variant doRun(const std::string &code) = 0; /** - * Implementation of the @see setVariable function. + * Implementation of the setVariable function. */ virtual void doSetVariable(const std::string &name, const Variant &val, - bool constant) = 0; + bool constant) = 0; /** - * Implementation of the @see getVariable function. + * Implementation of the getVariable function. */ virtual Variant doGetVariable(const std::string &name) = 0; public: - /** * Virtual destructor. Must be overwritten by implementing classes. */ - virtual ~ScriptEngineScope() {}; + virtual ~ScriptEngineScope(){}; /** * Runs the given code in the excution context. @@ -124,10 +118,7 @@ public: * @return a variant containg the result of the executed code. * @throws ScriptEngineException if an error occured during code execution. */ - Variant run(const std::string &code) - { - return doRun(code); - } + Variant run(const std::string &code) { return doRun(code); } /** * Sets the value of a variable in the scope with the given name. @@ -140,7 +131,7 @@ public: * @throws ScriptEngineException if name is not a well-formed identifier. */ void setVariable(const std::string &name, const Variant &val, - bool constant = false) + bool constant = false) { checkIdentifier(name); doSetVariable(name, val, constant); @@ -159,7 +150,6 @@ public: checkIdentifier(name); return doGetVariable(name); } - }; /** @@ -168,14 +158,12 @@ public: * function which creates an execution scope. */ class ScriptEngine { - public: /** * Requests an execution scope from the script engine implementation. The * calling code is responsible for disposing the returned pointer. */ - virtual ScriptEngineScope* createScope() const = 0; - + virtual ScriptEngineScope *createScope() = 0; }; /** @@ -184,16 +172,14 @@ public: * language. */ class ScriptEngineFactory { - private: /** * Internal map between the script language name and the actual script * engine instance. */ - std::map registry; + std::map registry; public: - /** * Registers a ScriptEngine instance for a new scripting language. * @@ -206,7 +192,7 @@ public: /** * Removes a script engine from the registry. * - * @param name is the name of the script engine that + * @param name is the name of the script engine that */ bool unregisterScriptEngine(const std::string &name); @@ -218,16 +204,12 @@ public: * is being created. * @return a pointer to the new execution scope or null if a script engine * with the given name does not exist. The caller of this function is - * responsible + * responsible */ - ScriptEngineScope* createScope(const std::string &name) const; - + ScriptEngineScope *createScope(const std::string &name) const; }; - } } - #endif /* _OUSIA_SCRIPT_ENGINE_HPP_ */ - diff --git a/src/core/utils/RangeSet.hpp b/src/core/utils/RangeSet.hpp new file mode 100644 index 0000000..841d476 --- /dev/null +++ b/src/core/utils/RangeSet.hpp @@ -0,0 +1,326 @@ +/* + Ousía + Copyright (C) 2014 Benjamin Paaßen, Andreas Stöckel + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _OUSIA_MODEL_RANGE_SET_HPP_ +#define _OUSIA_MODEL_RANGE_SET_HPP_ + +#include +#include + +namespace ousia { +namespace model { +/** + * The Range structure represents an interval of numerical values of type T. + */ +template +struct Range { + /** + * Start is the start value of the range. + */ + T start; + + /** + * End is the end value of the range (inclusively). + */ + T end; + + /** + * Default constructor of the range class. The range is initialized as + * invalid, with start being set to the maximum possible value of the + * numerical type T, and end being set to the minimum possible value. + */ + Range() : + start(std::numeric_limits::max()), + end(std::numeric_limits::min()) + { + // Do nothing here + } + + /** + * Copies the given start and end value. The given values are not checked + * for validity. Use the "isValid" + * + * @param start is the minimum value the range still covers. + * @param end is the maximum value the range still covers. + */ + Range(const T &start, const T &end) : + start(start), end(end) + { + // Do nothing here + } + + /** + * Creates a range that covers exactly one element, namely the value given + * as parameter n. + */ + Range(const T &n) : + start(n), end(n) + { + // Do nothing here + } + + /** + * Returns true if this range is valid, e.g. its start value is smaller or + * equal to its end value. + * + * @return true if start is smaller or equal to end, false otherwise. + */ + bool isValid() const + { + return start <= end; + } + + /** + * Checks whether the given value lies inside the range. + * + * @param v is the value that is being checked. + * @return true if the value lies within the range, false otherwise. + */ + bool inRange(T v) const + { + return (v >= start) && (v <= end); + } + + /** + * Checks whether the given range overlaps with another range. Not that + * this check is only meaningful if both ranges are valid. + * + * @param r is the range that should be checked for overlapping with this + * range. + */ + bool overlaps(const Range &r) const + { + return (((r.start >= start) || (r.end >= start)) + && ((r.start <= end) || (r.end <= end))); + } + + /** + * Returns true if the two given ranges are neighbours (their limits only + * differ in the smallest representable difference between them). + */ + bool neighbours(const Range &r) const + { + constexpr T eps = std::numeric_limits::is_integer + ? 1 : std::numeric_limits::epsilon(); + return ((r.start > end) && ((r.start - eps) <= end)) + || ((r.end < start) && ((r.end + eps) >= start)); + } + + /** + * Checks whether the given range completely covers this range. + */ + bool coveredBy(const Range &r) const + { + return (r.start <= start) && (r.end >= end); + } + + /** + * Checks whether this range completely covers the given range. + */ + bool covers(const Range &r) const + { + return r.coveredBy(*this); + } + + /** + * Calculates the union of the two ranges -- not that this operation is only + * valid if the ranges overlapp. Use the RangeSet class if you cannot + * guarantee that. + */ + Range merge(const Range &r) const + { + return Range(std::min(start, r.start), std::max(end, r.end)); + } + + /** + * Returns a range that represents the spans the complete set defined by the + * given type T. + */ + static Range typeRange() + { + return Range(std::numeric_limits::min(), + std::numeric_limits::max()); + } + + /** + * Returns a range that represents the spans the complete set defined by the + * given type T up to a given value. + * + * @param till is the value up to which the range should be defined (till is + * included in the set). + */ + static Range typeRangeUntil(const T &till) + { + return Range(std::numeric_limits::min(), till); + } + + /** + * Returns a range that represents the spans the complete set defined by the + * given type T up to a given value. + * + * @param from is the value from which the range should be defined (from is + * included in the set). + */ + static Range typeRangeFrom(const T &from) + { + return Range(from, std::numeric_limits::max()); + } +}; + +/** + * RangeComp is a comperator used to order to sort the ranges within the + * ranges list. Sorts by the start element. + */ +template +struct RangeComp { + bool operator() (const Range& lhs, const Range& rhs) const + { + return lhs.start < rhs.start; + } +}; + +/** + * RangeSet represents a set of ranges of the given numerical type and is thus + * capable of representing any possible subset of the given numerical type T. + */ +template +class RangeSet { + +protected: + /** + * Set of ranges used internally. + */ + std::set, RangeComp> ranges; + + /** + * Returns an iterator to the first element in the ranges list that overlaps + * with the given range. + * + * @param r is the range for which the first overlapping element should be + * found. + * @return an iterator pointing to the first overlapping element or to the + * end of the list if no such element was found. + */ + typename std::set, RangeComp>::iterator firstOverlapping( + const Range &r, const bool allowNeighbours) + { + // Find the element with the next larger start value compared to the + // start value given in r. + auto it = ranges.upper_bound(r); + + // Go back one element + if (it != ranges.begin()) { + it--; + } + + // Iterate until an overlapping element is found + while (!(it->overlaps(r) || (allowNeighbours && it->neighbours(r))) + && (it != ranges.end())) { + it++; + } + return it; + } + +public: + /** + * Calculates the union of this range set and the given range. + * + * @param range is the range that should be merged into this range set. + */ + void merge(Range r) + { + // Calculate a new range that covers both the new range and all old + // ranges in the set -- delete all old elements on the way + auto it = firstOverlapping(r, true); + while ((it->overlaps(r) || it->neighbours(r)) && it != ranges.end()) { + r = r.merge(*it); + it = ranges.erase(it); + } + + // Insert the new range + ranges.insert(r); + } + + /** + * Calculates the union of this range set and the given range set. + * + * @param ranges is another range set for which the union with this set + * should be calculated. + */ + void merge(const RangeSet &s) + { + for (Range &r : s.ranges) { + merge(r); + } + } + + /** + * Checks whether this range set S contains the given range R: + * S u R = R + * (The intersection between R and S equals the given range) + * + * @param r is the range for which the containment should be checked. + * @return true if the above condition is met, false otherwise. + */ + bool contains(const Range &r) + { + auto it = firstOverlapping(r, false); + if (it != ranges.end()) { + return (*it).covers(r); + } + return false; + } + + /** + * Checks whether this range set S1 contains the given range set S2: + * + * @param s is the range for which the containment should be checked. + * @return true if the above condition is met, false otherwise. + */ + bool contains(const RangeSet &s) + { + bool res = true; + for (Range &r : s.ranges) { + res = res && contains(r); + } + return res; + } + + /** + * Empties the set. + */ + void clear() + { + ranges.clear(); + } + + /** + * Returns the current list of ranges as a const reference. + */ + const std::set, RangeComp>& getRanges() + { + return this->ranges; + } + +}; + +} +} + +#endif /* _OUSIA_MODEL_RANGE_SET_HPP_ */ + diff --git a/src/plugins/mozjs/MozJsScriptEngine.cpp b/src/plugins/mozjs/MozJsScriptEngine.cpp new file mode 100644 index 0000000..b7ff002 --- /dev/null +++ b/src/plugins/mozjs/MozJsScriptEngine.cpp @@ -0,0 +1,218 @@ +/* + Ousía + Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "MozJsScriptEngine.hpp" + +namespace ousia { +namespace script { + +/* + * Some important links to the SpiderMonkey (mozjs) documentation: + * + * Documentation overview: + * https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/ + * + * User Guide: + * https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_User_Guide + * + * API Reference: + * https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_reference + */ + +/* Constants */ + +static const uint32_t MOZJS_RT_MEMSIZE = 64L * 1024L * 1024L; +static const uint32_t MOZJS_CTX_STACK_CHUNK_SIZE = 8192; + +/* Class MozJsScriptEngineScope */ + +/** + * The class of the global object. + */ +static JSClass globalClass = { + "global", JSCLASS_GLOBAL_FLAGS, JS_PropertyStub, + JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, + nullptr, nullptr, nullptr, + nullptr, nullptr}; + +MozJsScriptEngineScope::MozJsScriptEngineScope(JSRuntime *rt) : rt(rt) +{ + // Create the execution context + cx = JS_NewContext(rt, MOZJS_CTX_STACK_CHUNK_SIZE); + if (!cx) { + throw ScriptEngineException{"MozJs JS_NewContext failed"}; + } + + // Start a context request + JS_BeginRequest(cx); + + // Set some context options + JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_EXTRA_WARNINGS | + JSOPTION_VAROBJFIX | JSOPTION_DONT_REPORT_UNCAUGHT); + + // Create the rooted global object + global = + new JS::RootedObject(cx, JS_NewGlobalObject(cx, &globalClass, nullptr)); + + // Enter a compartment (heap memory region) surrounding the global object + oldCompartment = JS_EnterCompartment(cx, *global); + + // Populate the global object with the standard classes + if (!JS_InitStandardClasses(cx, *global)) { + throw ScriptEngineException{"MozJS JS_InitStandardClasses failed"}; + } +} + +MozJsScriptEngineScope::~MozJsScriptEngineScope() +{ + // Leave the compartment + JS_LeaveCompartment(cx, oldCompartment); + + // Free the reference to the local object + delete global; + + // End the request + JS_EndRequest(cx); + + // Destroy the execution context + JS_DestroyContext(cx); +} + +Variant MozJsScriptEngineScope::toVariant(const JS::Value &val) +{ + if (val.isBoolean()) { + return Variant{val.toBoolean()}; + } + if (val.isInt32()) { + return Variant{(int64_t)val.toInt32()}; + } + if (val.isDouble()) { + return Variant{val.toDouble()}; + } + if (val.isString()) { + // TODO: Remove the need for using "c_str"! + return Variant{toString(val.toString()).c_str()}; + } + return Variant::Null; +} + +void MozJsScriptEngineScope::handleErr(bool ok) +{ + if (!ok && JS_IsExceptionPending(cx)) { + JS::Value exception; + if (JS_GetPendingException(cx, &exception)) { + // Fetch messgage string, line and column + JS::Value msg, line, col; + JS_GetPendingException(cx, &exception); + JS_GetProperty(cx, JSVAL_TO_OBJECT(exception), "message", &msg); + JS_GetProperty(cx, JSVAL_TO_OBJECT(exception), "lineNumber", &line); + JS_GetProperty(cx, JSVAL_TO_OBJECT(exception), "columnNumber", + &col); + + // Clear the exception + JS_ClearPendingException(cx); + + // Produce a nice error message in case the caught exception is of + // the "Error" class + if (msg.isString() && line.isInt32() && col.isInt32()) { + // Throw a script engine exception with the corresponding line, + // column and string + throw ScriptEngineException{line.toInt32(), col.toInt32(), + toString(msg)}; + } + + // Otherwise simply convert the exception to a string + throw ScriptEngineException{toString(exception)}; + } + } +} + +std::string MozJsScriptEngineScope::toString(const JS::Value &val) +{ + // If the given value already is a Javascript string, return it directly. + if (val.isString()) { + return toString(val.toString()); + } + + // The given value is not really a string, so convert it to one first + JSString *str = JS_ValueToString(cx, val); + if (!str) { + throw ScriptEngineException{"Cannot convert value to string"}; + } + + return toString(str); +} + +std::string MozJsScriptEngineScope::toString(JSString *str) +{ + // Encode the string + char *buf = JS_EncodeStringToUTF8(cx, str); + if (!buf) { + throw ScriptEngineException{"JS_EncodeStringToUTF8 failed"}; + } + + // Copy the string into a std::string, free the original buffer and return + std::string res{buf}; + JS_free(cx, buf); + return res; +} + +Variant MozJsScriptEngineScope::doRun(const std::string &code) +{ + JS::Value rval; + handleErr(JS_EvaluateScript(cx, *global, code.c_str(), code.length(), "", 0, + &rval)); + return toVariant(rval); +} + +void MozJsScriptEngineScope::doSetVariable(const std::string &name, + const Variant &val, bool constant) +{ + // TODO +} + +Variant MozJsScriptEngineScope::doGetVariable(const std::string &name) +{ + // TODO + return Variant::Null; +} + +/* Class MozJsScriptEngine */ + +MozJsScriptEngine::MozJsScriptEngine() +{ + rt = JS_NewRuntime(MOZJS_RT_MEMSIZE, JS_NO_HELPER_THREADS); + if (!rt) { + throw ScriptEngineException{"MozJs JS_NewRuntime failed"}; + } +} + +MozJsScriptEngine::~MozJsScriptEngine() +{ + JS_DestroyRuntime(rt); + JS_ShutDown(); +} + +MozJsScriptEngineScope *MozJsScriptEngine::createScope() +{ + return new MozJsScriptEngineScope(rt); +} +} +} + diff --git a/src/plugins/mozjs/MozJsScriptEngine.hpp b/src/plugins/mozjs/MozJsScriptEngine.hpp new file mode 100644 index 0000000..68eee46 --- /dev/null +++ b/src/plugins/mozjs/MozJsScriptEngine.hpp @@ -0,0 +1,76 @@ +/* + Ousía + Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _MOZ_JS_SCRIPT_ENGINE_HPP_ +#define _MOZ_JS_SCRIPT_ENGINE_HPP_ + +#include + +#include + +namespace ousia { +namespace script { + +class MozJsScriptEngineScope : public ScriptEngineScope { + +private: + JSRuntime *rt; + JSContext *cx; + JSCompartment *oldCompartment; + JS::RootedObject *global; + + void handleErr(bool ok); + + Variant toVariant(const JS::Value &val); + + std::string toString(const JS::Value &val); + + std::string toString(JSString *str); + +protected: + Variant doRun(const std::string &code) override; + void doSetVariable(const std::string &name, const Variant &val, + bool constant) override; + Variant doGetVariable(const std::string &name) override; + +public: + MozJsScriptEngineScope(JSRuntime *rt); + + ~MozJsScriptEngineScope() override; + +}; + +class MozJsScriptEngine : public ScriptEngine { + +private: + JSRuntime *rt; + +public: + + MozJsScriptEngine(); + + ~MozJsScriptEngine(); + + MozJsScriptEngineScope *createScope() override; + +}; +} +} + +#endif /* _MOZ_JS_SCRIPT_ENGINE_HPP_ */ + -- cgit v1.2.3