From 3742c169b531e133d9c3462ecaf8b3f438c1be2b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 22 Oct 2015 12:03:19 -0700 Subject: [PATCH 01/10] Make handler generators more readable --- libraries/script-engine/src/ScriptEngine.cpp | 47 +++++++++----------- libraries/script-engine/src/ScriptEngine.h | 2 +- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 76590f266b..a317f2a82c 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -508,26 +508,29 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& // Connect up ALL the handlers to the global entities object's signals. // (We could go signal by signal, or even handler by handler, but I don't think the efficiency is worth the complexity.) auto entities = DependencyManager::get(); - connect(entities.data(), &EntityScriptingInterface::deletingEntity, this, - [=](const EntityItemID& entityID) { - _registeredHandlers.remove(entityID); - }); - + connect(entities.data(), &EntityScriptingInterface::deletingEntity, this, [this](const EntityItemID& entityID) { + _registeredHandlers.remove(entityID); + }); + // Two common cases of event handler, differing only in argument signature. - auto makeSingleEntityHandler = [=](const QString& eventName) -> std::function { - return [=](const EntityItemID& entityItemID) -> void { - generalHandler(entityItemID, eventName, [=]() -> QScriptValueList { - return QScriptValueList() << entityItemID.toScriptValue(this); - }); + auto makeSingleEntityHandler = [&](QString eventName) { + return [this, eventName](const EntityItemID& entityItemID) { + forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this) }); }; }; - auto makeMouseHandler = [=](const QString& eventName) -> std::function { - return [=](const EntityItemID& entityItemID, const MouseEvent& event) -> void { - generalHandler(entityItemID, eventName, [=]() -> QScriptValueList { - return QScriptValueList() << entityItemID.toScriptValue(this) << event.toScriptValue(this); - }); + auto makeMouseHandler = [&](QString eventName) { + return [this, eventName](const EntityItemID& entityItemID, const MouseEvent& event) { + forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) }); }; }; + + auto makeCollisionHandler = [&](QString eventName) { + return [this, eventName](const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) { + forwardHandlerCall(idA, eventName, { idA.toScriptValue(this), idB.toScriptValue(this), + collisionToScriptValue(this, collision) }); + }; + }; + connect(entities.data(), &EntityScriptingInterface::enterEntity, this, makeSingleEntityHandler("enterEntity")); connect(entities.data(), &EntityScriptingInterface::leaveEntity, this, makeSingleEntityHandler("leaveEntity")); @@ -543,12 +546,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& connect(entities.data(), &EntityScriptingInterface::hoverOverEntity, this, makeMouseHandler("hoverOverEntity")); connect(entities.data(), &EntityScriptingInterface::hoverLeaveEntity, this, makeMouseHandler("hoverLeaveEntity")); - connect(entities.data(), &EntityScriptingInterface::collisionWithEntity, this, - [=](const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) { - generalHandler(idA, "collisionWithEntity", [=]() { - return QScriptValueList () << idA.toScriptValue(this) << idB.toScriptValue(this) << collisionToScriptValue(this, collision); - }); - }); + connect(entities.data(), &EntityScriptingInterface::collisionWithEntity, this, makeCollisionHandler("collisionWithEntity")); } if (!_registeredHandlers.contains(entityID)) { _registeredHandlers[entityID] = RegisteredEventHandlers(); @@ -899,9 +897,9 @@ void ScriptEngine::load(const QString& loadFile) { } // Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args -void ScriptEngine::generalHandler(const EntityItemID& entityID, const QString& eventName, std::function argGenerator) { +void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs) { if (QThread::currentThread() != thread()) { - qDebug() << "*** ERROR *** ScriptEngine::generalHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; + qDebug() << "*** ERROR *** ScriptEngine::forwardHandlerCall() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; assert(false); return; } @@ -915,9 +913,8 @@ void ScriptEngine::generalHandler(const EntityItemID& entityID, const QString& e } QScriptValueList handlersForEvent = handlersOnEntity[eventName]; if (!handlersForEvent.isEmpty()) { - QScriptValueList args = argGenerator(); for (int i = 0; i < handlersForEvent.count(); ++i) { - handlersForEvent[i].call(QScriptValue(), args); + handlersForEvent[i].call(QScriptValue(), eventHanderArgs); } } } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 1d3986143a..c2f9d966f1 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -193,7 +193,7 @@ private: ArrayBufferClass* _arrayBufferClass; QHash _registeredHandlers; - void generalHandler(const EntityItemID& entityID, const QString& eventName, std::function argGenerator); + void forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs); Q_INVOKABLE void entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success); static QSet _allKnownScriptEngines; From c8c9118d4d0f671ea7f7cc4ed9dc64bdb4eaae30 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 23 Oct 2015 15:40:56 -0700 Subject: [PATCH 02/10] Improve script engine error logging + some cleanup --- libraries/script-engine/src/ScriptEngine.cpp | 74 ++++++++++++++------ libraries/script-engine/src/ScriptEngine.h | 5 +- libraries/shared/src/LogHandler.cpp | 2 +- 3 files changed, 56 insertions(+), 25 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index a317f2a82c..cbe9449551 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -556,7 +556,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& } -QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) { +QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fileName, int lineNumber) { if (_stoppingAllScripts) { return QScriptValue(); // bail early } @@ -565,27 +565,30 @@ QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileN QScriptValue result; #ifdef THREAD_DEBUGGING qDebug() << "*** WARNING *** ScriptEngine::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " - "program:" << program << " fileName:" << fileName << "lineNumber:" << lineNumber; + "sourceCode:" << sourceCode << " fileName:" << fileName << "lineNumber:" << lineNumber; #endif QMetaObject::invokeMethod(this, "evaluate", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QScriptValue, result), - Q_ARG(const QString&, program), + Q_ARG(const QString&, sourceCode), Q_ARG(const QString&, fileName), Q_ARG(int, lineNumber)); return result; } + + // Check synthax + const QScriptProgram program(sourceCode, fileName, lineNumber); + if (!checkSynthax(program)) { + return QScriptValue(); + } - _evaluatesPending++; - QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber); - if (hasUncaughtException()) { - int line = uncaughtExceptionLineNumber(); - qCDebug(scriptengine) << "Uncaught exception at (" << _fileNameString << " : " << fileName << ") line" << line << ": " << result.toString(); - } - _evaluatesPending--; + ++_evaluatesPending; + const auto result = QScriptEngine::evaluate(program); + --_evaluatesPending; + + const auto hadUncaughtException = checkExceptions(this, program.fileName()); if (_wantSignals) { - emit evaluationFinished(result, hasUncaughtException()); + emit evaluationFinished(result, hadUncaughtException); } - clearExceptions(); return result; } @@ -603,7 +606,7 @@ void ScriptEngine::run() { emit runningStateChanged(); } - QScriptValue result = evaluate(_scriptContents); + QScriptValue result = evaluate(_scriptContents, _fileNameString); QElapsedTimer startTime; startTime.start(); @@ -644,15 +647,6 @@ void ScriptEngine::run() { qint64 now = usecTimestampNow(); float deltaTime = (float) (now - lastUpdate) / (float) USECS_PER_SECOND; - if (hasUncaughtException()) { - int line = uncaughtExceptionLineNumber(); - qCDebug(scriptengine) << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << uncaughtException().toString(); - if (_wantSignals) { - emit errorMessage("Uncaught exception at (" + _fileNameString + ") line" + QString::number(line) + ":" + uncaughtException().toString()); - } - clearExceptions(); - } - if (!_isFinished) { if (_wantSignals) { emit update(deltaTime); @@ -660,6 +654,7 @@ void ScriptEngine::run() { } lastUpdate = now; + checkExceptions(this, _fileNameString); } stopAllTimers(); // make sure all our timers are stopped if the script is ending @@ -896,6 +891,38 @@ void ScriptEngine::load(const QString& loadFile) { } } + +bool ScriptEngine::checkSynthax(const QScriptProgram& program) { + const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); + if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { + const auto error = syntaxCheck.errorMessage(); + const auto line = QString::number(syntaxCheck.errorLineNumber()); + const auto column = QString::number(syntaxCheck.errorColumnNumber()); + const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, program.fileName(), line, column); + qCWarning(scriptengine) << qPrintable(message); + return false; + } + return true; +} + +bool ScriptEngine::checkExceptions(QScriptEngine* engine, const QString& fileName) { + if (engine->hasUncaughtException()) { + const auto backtrace = engine->uncaughtExceptionBacktrace(); + const auto exception = engine->uncaughtException().toString(); + const auto line = QString::number(engine->uncaughtExceptionLineNumber()); + engine->clearExceptions(); + + auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line); + if (!backtrace.empty()) { + static const auto lineSeparator = "\n "; + message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator)); + } + qCWarning(scriptengine) << qPrintable(message); + return false; + } + return true; +} + // Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs) { if (QThread::currentThread() != thread()) { @@ -1015,7 +1042,8 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co lastModified = (quint64)QFileInfo(file).lastModified().toMSecsSinceEpoch(); } - QScriptValue entityScriptConstructor = evaluate(contents); + auto fileName = QString("(EntityID:%1, %2)").arg(entityID.toString(), isURL ? scriptOrURL : "EmbededEntityScript"); + QScriptValue entityScriptConstructor = evaluate(contents, fileName); QScriptValue entityScriptObject = entityScriptConstructor.construct(); EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified }; _entityScripts[entityID] = newDetails; diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index c2f9d966f1..8fda876a31 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -88,7 +88,7 @@ public: Q_INVOKABLE void registerValue(const QString& valueName, QScriptValue value); /// evaluate some code in the context of the ScriptEngine and return the result - Q_INVOKABLE QScriptValue evaluate(const QString& program, const QString& fileName = QString(), int lineNumber = 1); // this is also used by the script tool widget + Q_INVOKABLE QScriptValue evaluate(const QString& program, const QString& fileName, int lineNumber = 1); // this is also used by the script tool widget /// if the script engine is not already running, this will download the URL and start the process of seting it up /// to run... NOTE - this is used by Application currently to load the url. We don't really want it to be exposed @@ -182,6 +182,9 @@ private: QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot); void stopTimer(QTimer* timer); + static bool checkSynthax(const QScriptProgram& program); + static bool checkExceptions(QScriptEngine* engine, const QString& fileName); + AbstractControllerScriptingInterface* _controllerScriptingInterface; QString _fileNameString; Quat _quatLibrary; diff --git a/libraries/shared/src/LogHandler.cpp b/libraries/shared/src/LogHandler.cpp index 22ea12c1b3..cc3519e43e 100644 --- a/libraries/shared/src/LogHandler.cpp +++ b/libraries/shared/src/LogHandler.cpp @@ -135,7 +135,7 @@ QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& cont prefixString.append(QString(" [%1]").arg(_targetName)); } - QString logMessage = QString("%1 %2").arg(prefixString, message); + QString logMessage = QString("%1 %2").arg(prefixString, message.split("\n").join("\n" + prefixString + " ")); fprintf(stdout, "%s\n", qPrintable(logMessage)); return logMessage; } From 13b7fa6b5df805497795725556f06cf0b9294731 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 26 Oct 2015 10:49:41 -0700 Subject: [PATCH 03/10] Typo --- libraries/script-engine/src/ScriptEngine.cpp | 6 +++--- libraries/script-engine/src/ScriptEngine.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index cbe9449551..2c725bdf70 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -575,9 +575,9 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi return result; } - // Check synthax + // Check syntax const QScriptProgram program(sourceCode, fileName, lineNumber); - if (!checkSynthax(program)) { + if (!checkSyntax(program)) { return QScriptValue(); } @@ -892,7 +892,7 @@ void ScriptEngine::load(const QString& loadFile) { } -bool ScriptEngine::checkSynthax(const QScriptProgram& program) { +bool ScriptEngine::checkSyntax(const QScriptProgram& program) { const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { const auto error = syntaxCheck.errorMessage(); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 8fda876a31..db10ecef40 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -182,7 +182,7 @@ private: QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot); void stopTimer(QTimer* timer); - static bool checkSynthax(const QScriptProgram& program); + static bool checkSyntax(const QScriptProgram& program); static bool checkExceptions(QScriptEngine* engine, const QString& fileName); AbstractControllerScriptingInterface* _controllerScriptingInterface; From 3e43a3c345b0f06f7789dbf57bb8885e260aba87 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 26 Oct 2015 11:33:13 -0700 Subject: [PATCH 04/10] Some more script checks --- libraries/script-engine/src/ScriptEngine.cpp | 26 +++++++------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 2c725bdf70..0411c507ab 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -79,7 +79,6 @@ void avatarDataFromScriptValue(const QScriptValue &object, AvatarData* &out) { QScriptValue inputControllerToScriptValue(QScriptEngine *engine, AbstractInputController* const &in) { return engine->newQObject(in); } - void inputControllerFromScriptValue(const QScriptValue &object, AbstractInputController* &out) { out = qobject_cast(object.toQObject()); } @@ -95,9 +94,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _wantSignals(wantSignals), _controllerScriptingInterface(controllerScriptingInterface), _fileNameString(fileNameString), - _quatLibrary(), - _vec3Library(), - _uuidLibrary(), _isUserLoaded(false), _isReloading(false), _arrayBufferClass(new ArrayBufferClass(this)) @@ -654,7 +650,9 @@ void ScriptEngine::run() { } lastUpdate = now; - checkExceptions(this, _fileNameString); + if (!checkExceptions(this, _fileNameString)) { + stop(); + } } stopAllTimers(); // make sure all our timers are stopped if the script is ending @@ -891,7 +889,6 @@ void ScriptEngine::load(const QString& loadFile) { } } - bool ScriptEngine::checkSyntax(const QScriptProgram& program) { const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { @@ -1005,14 +1002,10 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co auto scriptCache = DependencyManager::get(); bool isFileUrl = isURL && scriptOrURL.startsWith("file://"); + auto fileName = QString("(EntityID:%1, %2)").arg(entityID.toString(), isURL ? scriptOrURL : "EmbededEntityScript"); - // first check the syntax of the script contents - QScriptSyntaxCheckResult syntaxCheck = QScriptEngine::checkSyntax(contents); - if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { - qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID; - qCDebug(scriptengine) << " " << syntaxCheck.errorMessage() << ":" - << syntaxCheck.errorLineNumber() << syntaxCheck.errorColumnNumber(); - qCDebug(scriptengine) << " SCRIPT:" << scriptOrURL; + QScriptProgram program(contents, fileName); + if (!checkSyntax(program)) { if (!isFileUrl) { scriptCache->addScriptToBadScriptList(scriptOrURL); } @@ -1027,9 +1020,9 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co QScriptValue testConstructor = sandbox.evaluate(contents); if (!testConstructor.isFunction()) { - qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID; - qCDebug(scriptengine) << " NOT CONSTRUCTOR"; - qCDebug(scriptengine) << " SCRIPT:" << scriptOrURL; + qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID << "\n" + " NOT CONSTRUCTOR\n" + " SCRIPT:" << scriptOrURL; if (!isFileUrl) { scriptCache->addScriptToBadScriptList(scriptOrURL); } @@ -1042,7 +1035,6 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co lastModified = (quint64)QFileInfo(file).lastModified().toMSecsSinceEpoch(); } - auto fileName = QString("(EntityID:%1, %2)").arg(entityID.toString(), isURL ? scriptOrURL : "EmbededEntityScript"); QScriptValue entityScriptConstructor = evaluate(contents, fileName); QScriptValue entityScriptObject = entityScriptConstructor.construct(); EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified }; From 1bebdc8e7022890310e87e73e29521ed2b4df53c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 26 Oct 2015 11:33:36 -0700 Subject: [PATCH 05/10] Fix scripts syntax errors --- examples/libraries/entityCameraTool.js | 10 +++++----- examples/libraries/soundArray.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/libraries/entityCameraTool.js b/examples/libraries/entityCameraTool.js index d304a6382e..88e01b29fe 100644 --- a/examples/libraries/entityCameraTool.js +++ b/examples/libraries/entityCameraTool.js @@ -564,12 +564,12 @@ CameraTool = function(cameraManager) { var ORIENTATION_OVERLAY_SIZE = 26; var ORIENTATION_OVERLAY_HALF_SIZE = ORIENTATION_OVERLAY_SIZE / 2; - var ORIENTATION_OVERLAY_CUBE_SIZE = 10.5, + var ORIENTATION_OVERLAY_CUBE_SIZE = 10.5; - var ORIENTATION_OVERLAY_OFFSET = { - x: 30, - y: 30, - } + var ORIENTATION_OVERLAY_OFFSET = { + x: 30, + y: 30, + } var UI_WIDTH = 70; var UI_HEIGHT = 70; diff --git a/examples/libraries/soundArray.js b/examples/libraries/soundArray.js index 813621fb4b..f59c88a723 100644 --- a/examples/libraries/soundArray.js +++ b/examples/libraries/soundArray.js @@ -6,7 +6,7 @@ SoundArray = function(audioOptions, autoUpdateAudioPosition) { this.audioOptions = audioOptions !== undefined ? audioOptions : {}; this.autoUpdateAudioPosition = autoUpdateAudioPosition !== undefined ? autoUpdateAudioPosition : false; if (this.audioOptions.position === undefined) { - this.audioOptions.position = Vec3.sum(MyAvatar.position, { x: 0, y: 1, z: 0}), + this.audioOptions.position = Vec3.sum(MyAvatar.position, { x: 0, y: 1, z: 0}); } if (this.audioOptions.volume === undefined) { this.audioOptions.volume = 1.0; From ea56f965a48ddc368f2e38ccf9b10dd2364baee4 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 26 Oct 2015 12:27:48 -0700 Subject: [PATCH 06/10] Lambda conversion fix for windows --- libraries/script-engine/src/ScriptEngine.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0411c507ab..bcfeef1acb 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -509,18 +509,22 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& }); // Two common cases of event handler, differing only in argument signature. - auto makeSingleEntityHandler = [&](QString eventName) { + using SingleEntityHandler = std::function; + auto makeSingleEntityHandler = [this](QString eventName) -> SingleEntityHandler { return [this, eventName](const EntityItemID& entityItemID) { forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this) }); }; }; - auto makeMouseHandler = [&](QString eventName) { + + using MouseHandler = std::function; + auto makeMouseHandler = [this](QString eventName) -> MouseHandler { return [this, eventName](const EntityItemID& entityItemID, const MouseEvent& event) { forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) }); }; }; - auto makeCollisionHandler = [&](QString eventName) { + using CollisionHandler = std::function; + auto makeCollisionHandler = [this](QString eventName) -> CollisionHandler { return [this, eventName](const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) { forwardHandlerCall(idA, eventName, { idA.toScriptValue(this), idB.toScriptValue(this), collisionToScriptValue(this, collision) }); From a53a576aa32901ab4d5af7e4fa45cab99f30f1e9 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 26 Oct 2015 12:33:42 -0700 Subject: [PATCH 07/10] checkExceptions after testing entity scripts in the sandbox --- libraries/script-engine/src/ScriptEngine.cpp | 23 +++++++++++--------- libraries/script-engine/src/ScriptEngine.h | 2 +- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index bcfeef1acb..986613c20b 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -585,7 +585,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi const auto result = QScriptEngine::evaluate(program); --_evaluatesPending; - const auto hadUncaughtException = checkExceptions(this, program.fileName()); + const auto hadUncaughtException = checkExceptions(*this, program.fileName()); if (_wantSignals) { emit evaluationFinished(result, hadUncaughtException); } @@ -654,7 +654,7 @@ void ScriptEngine::run() { } lastUpdate = now; - if (!checkExceptions(this, _fileNameString)) { + if (!checkExceptions(*this, _fileNameString)) { stop(); } } @@ -906,12 +906,12 @@ bool ScriptEngine::checkSyntax(const QScriptProgram& program) { return true; } -bool ScriptEngine::checkExceptions(QScriptEngine* engine, const QString& fileName) { - if (engine->hasUncaughtException()) { - const auto backtrace = engine->uncaughtExceptionBacktrace(); - const auto exception = engine->uncaughtException().toString(); - const auto line = QString::number(engine->uncaughtExceptionLineNumber()); - engine->clearExceptions(); +bool ScriptEngine::checkExceptions(QScriptEngine& engine, const QString& fileName) { + if (engine.hasUncaughtException()) { + const auto backtrace = engine.uncaughtExceptionBacktrace(); + const auto exception = engine.uncaughtException().toString(); + const auto line = QString::number(engine.uncaughtExceptionLineNumber()); + engine.clearExceptions(); auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line); if (!backtrace.empty()) { @@ -1021,8 +1021,11 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co } QScriptEngine sandbox; - QScriptValue testConstructor = sandbox.evaluate(contents); - + QScriptValue testConstructor = sandbox.evaluate(program); + if (!checkExceptions(sandbox, program.fileName())) { + return; + } + if (!testConstructor.isFunction()) { qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID << "\n" " NOT CONSTRUCTOR\n" diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index db10ecef40..ddd957e163 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -183,7 +183,7 @@ private: void stopTimer(QTimer* timer); static bool checkSyntax(const QScriptProgram& program); - static bool checkExceptions(QScriptEngine* engine, const QString& fileName); + static bool checkExceptions(QScriptEngine& engine, const QString& fileName); AbstractControllerScriptingInterface* _controllerScriptingInterface; QString _fileNameString; From ffe73348fb9157186005b9e93e8ef24cb96a5a99 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 27 Oct 2015 12:10:45 -0700 Subject: [PATCH 08/10] Move check functions to cpp only --- libraries/script-engine/src/ScriptEngine.cpp | 62 ++++++++++---------- libraries/script-engine/src/ScriptEngine.h | 3 - 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 986613c20b..92bc3da595 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -83,6 +83,37 @@ void inputControllerFromScriptValue(const QScriptValue &object, AbstractInputCon out = qobject_cast(object.toQObject()); } +bool checkSyntax(const QScriptProgram& program) { + const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); + if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { + const auto error = syntaxCheck.errorMessage(); + const auto line = QString::number(syntaxCheck.errorLineNumber()); + const auto column = QString::number(syntaxCheck.errorColumnNumber()); + const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, program.fileName(), line, column); + qCWarning(scriptengine) << qPrintable(message); + return false; + } + return true; +} + +bool checkExceptions(QScriptEngine& engine, const QString& fileName) { + if (engine.hasUncaughtException()) { + const auto backtrace = engine.uncaughtExceptionBacktrace(); + const auto exception = engine.uncaughtException().toString(); + const auto line = QString::number(engine.uncaughtExceptionLineNumber()); + engine.clearExceptions(); + + auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line); + if (!backtrace.empty()) { + static const auto lineSeparator = "\n "; + message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator)); + } + qCWarning(scriptengine) << qPrintable(message); + return false; + } + return true; +} + ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, AbstractControllerScriptingInterface* controllerScriptingInterface, bool wantSignals) : @@ -893,37 +924,6 @@ void ScriptEngine::load(const QString& loadFile) { } } -bool ScriptEngine::checkSyntax(const QScriptProgram& program) { - const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); - if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { - const auto error = syntaxCheck.errorMessage(); - const auto line = QString::number(syntaxCheck.errorLineNumber()); - const auto column = QString::number(syntaxCheck.errorColumnNumber()); - const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, program.fileName(), line, column); - qCWarning(scriptengine) << qPrintable(message); - return false; - } - return true; -} - -bool ScriptEngine::checkExceptions(QScriptEngine& engine, const QString& fileName) { - if (engine.hasUncaughtException()) { - const auto backtrace = engine.uncaughtExceptionBacktrace(); - const auto exception = engine.uncaughtException().toString(); - const auto line = QString::number(engine.uncaughtExceptionLineNumber()); - engine.clearExceptions(); - - auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line); - if (!backtrace.empty()) { - static const auto lineSeparator = "\n "; - message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator)); - } - qCWarning(scriptengine) << qPrintable(message); - return false; - } - return true; -} - // Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs) { if (QThread::currentThread() != thread()) { diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index ddd957e163..89d651930b 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -181,9 +181,6 @@ private: QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot); void stopTimer(QTimer* timer); - - static bool checkSyntax(const QScriptProgram& program); - static bool checkExceptions(QScriptEngine& engine, const QString& fileName); AbstractControllerScriptingInterface* _controllerScriptingInterface; QString _fileNameString; From c0138bd189542116b323c1e75c75f68b7afea8c1 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 27 Oct 2015 12:39:40 -0700 Subject: [PATCH 09/10] Rename check functions and make them static --- libraries/script-engine/src/ScriptEngine.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 92bc3da595..1d6bf32fcc 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -83,7 +83,7 @@ void inputControllerFromScriptValue(const QScriptValue &object, AbstractInputCon out = qobject_cast(object.toQObject()); } -bool checkSyntax(const QScriptProgram& program) { +static bool hasCorrectSyntax(const QScriptProgram& program) { const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { const auto error = syntaxCheck.errorMessage(); @@ -96,7 +96,7 @@ bool checkSyntax(const QScriptProgram& program) { return true; } -bool checkExceptions(QScriptEngine& engine, const QString& fileName) { +static bool hadUncauchtExceptions(QScriptEngine& engine, const QString& fileName) { if (engine.hasUncaughtException()) { const auto backtrace = engine.uncaughtExceptionBacktrace(); const auto exception = engine.uncaughtException().toString(); @@ -109,9 +109,9 @@ bool checkExceptions(QScriptEngine& engine, const QString& fileName) { message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator)); } qCWarning(scriptengine) << qPrintable(message); - return false; + return true; } - return true; + return false; } ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, @@ -608,7 +608,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi // Check syntax const QScriptProgram program(sourceCode, fileName, lineNumber); - if (!checkSyntax(program)) { + if (!hasCorrectSyntax(program)) { return QScriptValue(); } @@ -616,7 +616,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi const auto result = QScriptEngine::evaluate(program); --_evaluatesPending; - const auto hadUncaughtException = checkExceptions(*this, program.fileName()); + const auto hadUncaughtException = hadUncauchtExceptions(*this, program.fileName()); if (_wantSignals) { emit evaluationFinished(result, hadUncaughtException); } @@ -685,7 +685,7 @@ void ScriptEngine::run() { } lastUpdate = now; - if (!checkExceptions(*this, _fileNameString)) { + if (hadUncauchtExceptions(*this, _fileNameString)) { stop(); } } @@ -1009,7 +1009,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co auto fileName = QString("(EntityID:%1, %2)").arg(entityID.toString(), isURL ? scriptOrURL : "EmbededEntityScript"); QScriptProgram program(contents, fileName); - if (!checkSyntax(program)) { + if (!hasCorrectSyntax(program)) { if (!isFileUrl) { scriptCache->addScriptToBadScriptList(scriptOrURL); } @@ -1022,7 +1022,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co QScriptEngine sandbox; QScriptValue testConstructor = sandbox.evaluate(program); - if (!checkExceptions(sandbox, program.fileName())) { + if (hadUncauchtExceptions(sandbox, program.fileName())) { return; } From 32f88f3fb6a664d5e664db51415fd8ecc0dd007d Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 27 Oct 2015 14:37:39 -0700 Subject: [PATCH 10/10] Fix for JSConcole --- interface/src/ui/JSConsole.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index b50f84d538..2d34ba5608 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -106,8 +106,11 @@ void JSConsole::executeCommand(const QString& command) { QScriptValue JSConsole::executeCommandInWatcher(const QString& command) { QScriptValue result; + static const QString filename = "JSConcole"; QMetaObject::invokeMethod(_scriptEngine, "evaluate", Qt::ConnectionType::BlockingQueuedConnection, - Q_RETURN_ARG(QScriptValue, result), Q_ARG(const QString&, command)); + Q_RETURN_ARG(QScriptValue, result), + Q_ARG(const QString&, command), + Q_ARG(const QString&, filename)); return result; }