diff options
author | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2014-10-25 21:23:38 +0000 |
---|---|---|
committer | andreas <andreas@daaaf23c-2e50-4459-9457-1e69db5a47bf> | 2014-10-25 21:23:38 +0000 |
commit | 250d6a4dbe61b6798cd090abeabdc0ece8237dd3 (patch) | |
tree | 05a15d388c8b8444b6ddd4c6806cd4f66169c2a0 /src | |
parent | dbb94be50c67ce2d4a132b0811c2a8dac825b49b (diff) |
some restructuring, added first version of the mozjs plugin
git-svn-id: file:///var/local/svn/basicwriter@78 daaaf23c-2e50-4459-9457-1e69db5a47bf
Diffstat (limited to 'src')
-rw-r--r-- | src/core/script/Object.hpp | 2 | ||||
-rw-r--r-- | src/core/script/ScriptEngine.cpp | 32 | ||||
-rw-r--r-- | src/core/script/ScriptEngine.hpp | 46 | ||||
-rw-r--r-- | src/core/utils/RangeSet.hpp (renamed from src/core/model/RangeSet.hpp) | 0 | ||||
-rw-r--r-- | src/plugins/mozjs/MozJsScriptEngine.cpp | 218 | ||||
-rw-r--r-- | src/plugins/mozjs/MozJsScriptEngine.hpp | 76 |
6 files changed, 327 insertions, 47 deletions
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<std::string, ScriptEngine*> registry; + std::map<std::string, ScriptEngine *> 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/model/RangeSet.hpp b/src/core/utils/RangeSet.hpp index 841d476..841d476 100644 --- a/src/core/model/RangeSet.hpp +++ b/src/core/utils/RangeSet.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 <http://www.gnu.org/licenses/>. +*/ + +#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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _MOZ_JS_SCRIPT_ENGINE_HPP_ +#define _MOZ_JS_SCRIPT_ENGINE_HPP_ + +#include <jsapi.h> + +#include <core/script/ScriptEngine.hpp> + +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_ */ + |