History

Merge branch 'master' into fix/script-engine-abuse

Ryan Huffman committed 9 years ago

Commit 601aa512 parents 51586ae fcd6c0a686 files · +48509 −6625Browse files

Showing the changes to libraries/script-engine/src/ScriptEngine.cpp (1 of 686 files). Show all

Unified · Split

libraries/script-engine/src/ScriptEngine.cpp+181−104
@@ -72,6 +72,8 @@ static const QScriptEngine::QObjectWrapOptions DEFAULT_QOBJECT_WRAP_OPTIONS = · 71 unmodified lines
72Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature)72Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature)
73int functionSignatureMetaID = qRegisterMetaType<QScriptEngine::FunctionSignature>();73int functionSignatureMetaID = qRegisterMetaType<QScriptEngine::FunctionSignature>();
74 74
75Q_LOGGING_CATEGORY(scriptengineScript, "hifi.scriptengine.script")
76
75static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){77static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){
76 QString message = "";78 QString message = "";
77 for (int i = 0; i < context->argumentCount(); i++) {79 for (int i = 0; i < context->argumentCount(); i++) {
@@ -80,12 +82,14 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){ · 2 unmodified lines
80 }82 }
81 message += context->argument(i).toString();83 message += context->argument(i).toString();
82 }84 }
83 qCDebug(scriptengine).noquote() << "script:print()<<" << message; // noquote() so that \n is treated as newline85 qCDebug(scriptengineScript).noquote() << "script:print()<<" << message; // noquote() so that \n is treated as newline
84 86
85 message = message.replace("\\", "\\\\")87 message = message.replace("\\", "\\\\")
86 .replace("\n", "\\n")88 .replace("\n", "\\n")
87 .replace("\r", "\\r")89 .replace("\r", "\\r")
88 .replace("'", "\\'");90 .replace("'", "\\'");
91
92 // FIXME - this approach neeeds revisiting. print() comes here, which ends up doing an evaluate?
89 engine->evaluate("Script.print('" + message + "')");93 engine->evaluate("Script.print('" + message + "')");
90 94
91 return QScriptValue();95 return QScriptValue();
@@ -130,20 +134,20 @@ QString encodeEntityIdIntoEntityUrl(const QString& url, const QString& entityID) · 38 unmodified lines
130 return url + " [EntityID:" + entityID + "]";134 return url + " [EntityID:" + entityID + "]";
131}135}
132 136
133static bool hasCorrectSyntax(const QScriptProgram& program) {137static bool hasCorrectSyntax(const QScriptProgram& program, ScriptEngine* reportingEngine) {
134 const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode());138 const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode());
135 if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {139 if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
136 const auto error = syntaxCheck.errorMessage();140 const auto error = syntaxCheck.errorMessage();
137 const auto line = QString::number(syntaxCheck.errorLineNumber());141 const auto line = QString::number(syntaxCheck.errorLineNumber());
138 const auto column = QString::number(syntaxCheck.errorColumnNumber());142 const auto column = QString::number(syntaxCheck.errorColumnNumber());
139 const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, program.fileName(), line, column);143 const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, program.fileName(), line, column);
140 qCWarning(scriptengine) << qPrintable(message);144 reportingEngine->scriptErrorMessage(qPrintable(message));
141 return false;145 return false;
142 }146 }
143 return true;147 return true;
144}148}
145 149
146static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName) {150static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName, ScriptEngine* reportingEngine) {
147 if (engine.hasUncaughtException()) {151 if (engine.hasUncaughtException()) {
148 const auto backtrace = engine.uncaughtExceptionBacktrace();152 const auto backtrace = engine.uncaughtExceptionBacktrace();
149 const auto exception = engine.uncaughtException().toString();153 const auto exception = engine.uncaughtException().toString();
@@ -155,7 +159,7 @@ static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName · 5 unmodified lines
155 static const auto lineSeparator = "\n ";159 static const auto lineSeparator = "\n ";
156 message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator));160 message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator));
157 }161 }
158 qCWarning(scriptengine) << qPrintable(message);162 reportingEngine->scriptErrorMessage(qPrintable(message));
159 return true;163 return true;
160 }164 }
161 return false;165 return false;
@@ -170,20 +174,20 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam · 8 unmodified lines
170 DependencyManager::get<ScriptEngines>()->addScriptEngine(this);174 DependencyManager::get<ScriptEngines>()->addScriptEngine(this);
171 175
172 connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) {176 connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) {
173 hadUncaughtExceptions(*this, _fileNameString);177 hadUncaughtExceptions(*this, _fileNameString, this);
174 });178 });
175 179
176 setProcessEventsInterval(MSECS_PER_SECOND);180 setProcessEventsInterval(MSECS_PER_SECOND);
177}181}
178 182
179ScriptEngine::~ScriptEngine() {183ScriptEngine::~ScriptEngine() {
180 qCDebug(scriptengine) << "Script Engine shutting down:" << getFilename();184 scriptInfoMessage("Script Engine shutting down:" + getFilename());
181 185
182 auto scriptEngines = DependencyManager::get<ScriptEngines>();186 auto scriptEngines = DependencyManager::get<ScriptEngines>();
183 if (scriptEngines) {187 if (scriptEngines) {
184 scriptEngines->removeScriptEngine(this);188 scriptEngines->removeScriptEngine(this);
185 } else {189 } else {
186 qCWarning(scriptengine) << "Script destroyed after ScriptEngines!";190 scriptWarningMessage("Script destroyed after ScriptEngines!");
187 }191 }
188}192}
189 193
@@ -274,7 +278,7 @@ void ScriptEngine::runDebuggable() { · 84 unmodified lines
274 }278 }
275 _lastUpdate = now;279 _lastUpdate = now;
276 // Debug and clear exceptions280 // Debug and clear exceptions
277 hadUncaughtExceptions(*this, _fileNameString);281 hadUncaughtExceptions(*this, _fileNameString, this);
278 });282 });
279 283
280 timer->start(10);284 timer->start(10);
@@ -359,7 +363,7 @@ void ScriptEngine::waitTillDoneRunning() { · 78 unmodified lines
359 QThread::yieldCurrentThread();363 QThread::yieldCurrentThread();
360 }364 }
361 365
362 qCDebug(scriptengine) << "Script Engine has stopped:" << getFilename();366 scriptInfoMessage("Script Engine has stopped:" + getFilename());
363 }367 }
364}368}
365 369
@@ -398,10 +402,25 @@ void ScriptEngine::scriptContentsAvailable(const QUrl& url, const QString& scrip · 32 unmodified lines
398 emit scriptLoaded(url.toString());402 emit scriptLoaded(url.toString());
399}403}
400 404
405void ScriptEngine::scriptErrorMessage(const QString& message) {
406 qCCritical(scriptengine) << message;
407 emit errorMessage(message);
408}
409
410void ScriptEngine::scriptWarningMessage(const QString& message) {
411 qCWarning(scriptengine) << message;
412 emit warningMessage(message);
413}
414
415void ScriptEngine::scriptInfoMessage(const QString& message) {
416 qCInfo(scriptengine) << message;
417 emit infoMessage(message);
418}
419
401// FIXME - switch this to the new model of ScriptCache callbacks420// FIXME - switch this to the new model of ScriptCache callbacks
402void ScriptEngine::errorInLoadingScript(const QUrl& url) {421void ScriptEngine::errorInLoadingScript(const QUrl& url) {
403 qCDebug(scriptengine) << "ERROR Loading file:" << url.toString() << "line:" << __LINE__;422 scriptErrorMessage("ERROR Loading file:" + url.toString());
404 emit errorLoadingScript(_fileNameString); // ??423 emit errorLoadingScript(_fileNameString);
405}424}
406 425
407// Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of426// Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of
@@ -553,7 +572,7 @@ void ScriptEngine::init() { · 145 unmodified lines
553void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) {572void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) {
554 if (QThread::currentThread() != thread()) {573 if (QThread::currentThread() != thread()) {
555#ifdef THREAD_DEBUGGING574#ifdef THREAD_DEBUGGING
556 qDebug() << "*** WARNING *** ScriptEngine::registerValue() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";575 qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::registerValue() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";
557#endif576#endif
558 QMetaObject::invokeMethod(this, "registerValue",577 QMetaObject::invokeMethod(this, "registerValue",
559 Q_ARG(const QString&, valueName),578 Q_ARG(const QString&, valueName),
@@ -583,7 +602,7 @@ void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { · 23 unmodified lines
583void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) {602void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) {
584 if (QThread::currentThread() != thread()) {603 if (QThread::currentThread() != thread()) {
585#ifdef THREAD_DEBUGGING604#ifdef THREAD_DEBUGGING
586 qDebug() << "*** WARNING *** ScriptEngine::registerGlobalObject() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name;605 qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::registerGlobalObject() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name;
587#endif606#endif
588 QMetaObject::invokeMethod(this, "registerGlobalObject",607 QMetaObject::invokeMethod(this, "registerGlobalObject",
589 Q_ARG(const QString&, name),608 Q_ARG(const QString&, name),
@@ -591,7 +610,7 @@ void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { · 1 unmodified line
591 return;610 return;
592 }611 }
593#ifdef THREAD_DEBUGGING612#ifdef THREAD_DEBUGGING
594 qDebug() << "ScriptEngine::registerGlobalObject() called on thread [" << QThread::currentThread() << "] name:" << name;613 qCDebug(scriptengine) << "ScriptEngine::registerGlobalObject() called on thread [" << QThread::currentThread() << "] name:" << name;
595#endif614#endif
596 615
597 if (!globalObject().property(name).isValid()) {616 if (!globalObject().property(name).isValid()) {
@@ -607,7 +626,7 @@ void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { · 9 unmodified lines
607void ScriptEngine::registerFunction(const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {626void ScriptEngine::registerFunction(const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {
608 if (QThread::currentThread() != thread()) {627 if (QThread::currentThread() != thread()) {
609#ifdef THREAD_DEBUGGING628#ifdef THREAD_DEBUGGING
610 qDebug() << "*** WARNING *** ScriptEngine::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name;629 qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name;
611#endif630#endif
612 QMetaObject::invokeMethod(this, "registerFunction",631 QMetaObject::invokeMethod(this, "registerFunction",
613 Q_ARG(const QString&, name),632 Q_ARG(const QString&, name),
@@ -616,7 +635,7 @@ void ScriptEngine::registerFunction(const QString& name, QScriptEngine::Function · 2 unmodified lines
616 return;635 return;
617 }636 }
618#ifdef THREAD_DEBUGGING637#ifdef THREAD_DEBUGGING
619 qDebug() << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] name:" << name;638 qCDebug(scriptengine) << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] name:" << name;
620#endif639#endif
621 640
622 QScriptValue scriptFun = newFunction(functionSignature, numArguments);641 QScriptValue scriptFun = newFunction(functionSignature, numArguments);
@@ -626,7 +645,7 @@ void ScriptEngine::registerFunction(const QString& name, QScriptEngine::Function · 3 unmodified lines
626void ScriptEngine::registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {645void ScriptEngine::registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {
627 if (QThread::currentThread() != thread()) {646 if (QThread::currentThread() != thread()) {
628#ifdef THREAD_DEBUGGING647#ifdef THREAD_DEBUGGING
629 qDebug() << "*** WARNING *** ScriptEngine::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] parent:" << parent << "name:" << name;648 qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] parent:" << parent << "name:" << name;
630#endif649#endif
631 QMetaObject::invokeMethod(this, "registerFunction",650 QMetaObject::invokeMethod(this, "registerFunction",
632 Q_ARG(const QString&, name),651 Q_ARG(const QString&, name),
@@ -635,7 +654,7 @@ void ScriptEngine::registerFunction(const QString& parent, const QString& name, · 2 unmodified lines
635 return;654 return;
636 }655 }
637#ifdef THREAD_DEBUGGING656#ifdef THREAD_DEBUGGING
638 qDebug() << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] parent:" << parent << "name:" << name;657 qCDebug(scriptengine) << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] parent:" << parent << "name:" << name;
639#endif658#endif
640 659
641 QScriptValue object = globalObject().property(parent);660 QScriptValue object = globalObject().property(parent);
@@ -649,7 +668,7 @@ void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::Func · 7 unmodified lines
649 QScriptEngine::FunctionSignature setter, const QString& parent) {668 QScriptEngine::FunctionSignature setter, const QString& parent) {
650 if (QThread::currentThread() != thread()) {669 if (QThread::currentThread() != thread()) {
651#ifdef THREAD_DEBUGGING670#ifdef THREAD_DEBUGGING
652 qDebug() << "*** WARNING *** ScriptEngine::registerGetterSetter() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "671 qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::registerGetterSetter() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
653 " name:" << name << "parent:" << parent;672 " name:" << name << "parent:" << parent;
654#endif673#endif
655 QMetaObject::invokeMethod(this, "registerGetterSetter",674 QMetaObject::invokeMethod(this, "registerGetterSetter",
@@ -660,7 +679,7 @@ void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::Func · 4 unmodified lines
660 return;679 return;
661 }680 }
662#ifdef THREAD_DEBUGGING681#ifdef THREAD_DEBUGGING
663 qDebug() << "ScriptEngine::registerGetterSetter() called on thread [" << QThread::currentThread() << "] name:" << name << "parent:" << parent;682 qCDebug(scriptengine) << "ScriptEngine::registerGetterSetter() called on thread [" << QThread::currentThread() << "] name:" << name << "parent:" << parent;
664#endif683#endif
665 684
666 QScriptValue setterFunction = newFunction(setter, 1);685 QScriptValue setterFunction = newFunction(setter, 1);
@@ -682,7 +701,7 @@ void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::Func · 15 unmodified lines
682void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) {701void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) {
683 if (QThread::currentThread() != thread()) {702 if (QThread::currentThread() != thread()) {
684#ifdef THREAD_DEBUGGING703#ifdef THREAD_DEBUGGING
685 qDebug() << "*** WARNING *** ScriptEngine::removeEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "704 qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::removeEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
686 "entityID:" << entityID << " eventName:" << eventName;705 "entityID:" << entityID << " eventName:" << eventName;
687#endif706#endif
688 QMetaObject::invokeMethod(this, "removeEventHandler",707 QMetaObject::invokeMethod(this, "removeEventHandler",
@@ -692,7 +711,7 @@ void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QStrin · 3 unmodified lines
692 return;711 return;
693 }712 }
694#ifdef THREAD_DEBUGGING713#ifdef THREAD_DEBUGGING
695 qDebug() << "ScriptEngine::removeEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName;714 qCDebug(scriptengine) << "ScriptEngine::removeEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName;
696#endif715#endif
697 716
698 if (!_registeredHandlers.contains(entityID)) {717 if (!_registeredHandlers.contains(entityID)) {
@@ -712,7 +731,7 @@ void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QStrin · 13 unmodified lines
712void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) {731void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) {
713 if (QThread::currentThread() != thread()) {732 if (QThread::currentThread() != thread()) {
714#ifdef THREAD_DEBUGGING733#ifdef THREAD_DEBUGGING
715 qDebug() << "*** WARNING *** ScriptEngine::addEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "734 qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::addEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
716 "entityID:" << entityID << " eventName:" << eventName;735 "entityID:" << entityID << " eventName:" << eventName;
717#endif736#endif
718 737
@@ -723,7 +742,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& · 4 unmodified lines
723 return;742 return;
724 }743 }
725#ifdef THREAD_DEBUGGING744#ifdef THREAD_DEBUGGING
726 qDebug() << "ScriptEngine::addEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName;745 qCDebug(scriptengine) << "ScriptEngine::addEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName;
727#endif746#endif
728 747
729 if (_registeredHandlers.count() == 0) { // First time any per-entity handler has been added in this script...748 if (_registeredHandlers.count() == 0) { // First time any per-entity handler has been added in this script...
@@ -785,7 +804,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& · 55 unmodified lines
785 _registeredHandlers[entityID] = RegisteredEventHandlers();804 _registeredHandlers[entityID] = RegisteredEventHandlers();
786 }805 }
787 CallbackList& handlersForEvent = _registeredHandlers[entityID][eventName];806 CallbackList& handlersForEvent = _registeredHandlers[entityID][eventName];
788 CallbackData handlerData = {handler, currentEntityIdentifier, currentSandboxURL};807 CallbackData handlerData = { handler, currentEntityIdentifier, currentSandboxURL };
789 handlersForEvent << handlerData; // Note that the same handler can be added many times. See removeEntityEventHandler().808 handlersForEvent << handlerData; // Note that the same handler can be added many times. See removeEntityEventHandler().
790}809}
791 810
@@ -798,7 +817,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi · 6 unmodified lines
798 if (QThread::currentThread() != thread()) {817 if (QThread::currentThread() != thread()) {
799 QScriptValue result;818 QScriptValue result;
800#ifdef THREAD_DEBUGGING819#ifdef THREAD_DEBUGGING
801 qDebug() << "*** WARNING *** ScriptEngine::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "820 qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
802 "sourceCode:" << sourceCode << " fileName:" << fileName << "lineNumber:" << lineNumber;821 "sourceCode:" << sourceCode << " fileName:" << fileName << "lineNumber:" << lineNumber;
803#endif822#endif
804 QMetaObject::invokeMethod(this, "evaluate", Qt::BlockingQueuedConnection,823 QMetaObject::invokeMethod(this, "evaluate", Qt::BlockingQueuedConnection,
@@ -811,7 +830,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi · 6 unmodified lines
811 830
812 // Check syntax831 // Check syntax
813 const QScriptProgram program(sourceCode, fileName, lineNumber);832 const QScriptProgram program(sourceCode, fileName, lineNumber);
814 if (!hasCorrectSyntax(program)) {833 if (!hasCorrectSyntax(program, this)) {
815 return QScriptValue();834 return QScriptValue();
816 }835 }
817 836
@@ -819,7 +838,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi · 1 unmodified line
819 const auto result = QScriptEngine::evaluate(program);838 const auto result = QScriptEngine::evaluate(program);
820 --_evaluatesPending;839 --_evaluatesPending;
821 840
822 const auto hadUncaughtException = hadUncaughtExceptions(*this, program.fileName());841 const auto hadUncaughtException = hadUncaughtExceptions(*this, program.fileName(), this);
823 emit evaluationFinished(result, hadUncaughtException);842 emit evaluationFinished(result, hadUncaughtException);
824 return result;843 return result;
825}844}
@@ -863,23 +882,48 @@ void ScriptEngine::run() { · 37 unmodified lines
863 // Throttle to SCRIPT_FPS882 // Throttle to SCRIPT_FPS
864 // We'd like to try to keep the script at a solid SCRIPT_FPS update rate. And so we will 883 // We'd like to try to keep the script at a solid SCRIPT_FPS update rate. And so we will
865 // calculate a sleepUntil to be the time from our start time until the original target884 // calculate a sleepUntil to be the time from our start time until the original target
866 // sleepUntil for this frame.885 // sleepUntil for this frame. This approach will allow us to "catch up" in the event
867 const std::chrono::microseconds FRAME_DURATION(USECS_PER_SECOND / SCRIPT_FPS + 1);886 // that some of our script udpates/frames take a little bit longer than the target average
868 clock::time_point targetSleepUntil(startTime + thisFrame++ * FRAME_DURATION);887 // to execute.
869 888 // NOTE: if we go to variable SCRIPT_FPS, then we will need to reconsider this approach
870 // However, if our sleepUntil is not at least our average update time into the future889 const std::chrono::microseconds TARGET_SCRIPT_FRAME_DURATION(USECS_PER_SECOND / SCRIPT_FPS + 1);
871 // it means our script is taking too long in it's updates, and we want to punish the890 clock::time_point targetSleepUntil(startTime + (thisFrame++ * TARGET_SCRIPT_FRAME_DURATION));
872 // script a little bit. So we will force the sleepUntil to be at least our averageUpdate891
873 // time into the future.892 // However, if our sleepUntil is not at least our average update and timer execution time
893 // into the future it means our script is taking too long in its updates, and we want to
894 // punish the script a little bit. So we will force the sleepUntil to be at least our
895 // averageUpdate + averageTimerPerFrame time into the future.
874 auto averageUpdate = totalUpdates / thisFrame;896 auto averageUpdate = totalUpdates / thisFrame;
875 auto sleepUntil = std::max(targetSleepUntil, beforeSleep + averageUpdate);897 auto averageTimerPerFrame = _totalTimerExecution / thisFrame;
898 auto averageTimerAndUpdate = averageUpdate + averageTimerPerFrame;
899 auto sleepUntil = std::max(targetSleepUntil, beforeSleep + averageTimerAndUpdate);
876 900
877 // We don't want to actually sleep for too long, because it causes our scripts to hang 901 // We don't want to actually sleep for too long, because it causes our scripts to hang
878 // on shutdown and stop... so we want to loop and sleep until we've spent our time in 902 // on shutdown and stop... so we want to loop and sleep until we've spent our time in
879 // purgatory, constantly checking to see if our script was asked to end903 // purgatory, constantly checking to see if our script was asked to end
904 bool processedEvents = false;
880 while (!_isFinished && clock::now() < sleepUntil) {905 while (!_isFinished && clock::now() < sleepUntil) {
906
881 QCoreApplication::processEvents(); // before we sleep again, give events a chance to process907 QCoreApplication::processEvents(); // before we sleep again, give events a chance to process
882 auto thisSleepUntil = std::min(sleepUntil, clock::now() + FRAME_DURATION);908 processedEvents = true;
909
910 // If after processing events, we're past due, exit asap
911 if (clock::now() >= sleepUntil) {
912 break;
913 }
914
915 // determine how long before the next timer should fire, we'd ideally like to sleep just
916 // that long, so the next processEvents() will allow the timers to fire on time.
917 const std::chrono::microseconds minTimerTimeRemaining(USECS_PER_MSEC * getTimersRemainingTime());
918
919 // However, if we haven't yet slept at least as long as our average timer per frame, then we will
920 // punish the timers to at least wait as long as the average run time of the timers.
921 auto untilTimer = std::max(minTimerTimeRemaining, averageTimerPerFrame);
922
923 // choose the closest time point, our
924 auto remainingSleepUntil = std::chrono::duration_cast<std::chrono::microseconds>(sleepUntil - clock::now());
925 auto closestUntil = std::min(remainingSleepUntil, untilTimer);
926 auto thisSleepUntil = std::min(sleepUntil, clock::now() + closestUntil);
883 std::this_thread::sleep_until(thisSleepUntil);927 std::this_thread::sleep_until(thisSleepUntil);
884 }928 }
885 929
@@ -904,7 +948,10 @@ void ScriptEngine::run() { · 18 unmodified lines
904 break;948 break;
905 }949 }
906 950
907 QCoreApplication::processEvents();951 // Only call this if we didn't processEvents as part of waiting for next frame
952 if (!processedEvents) {
953 QCoreApplication::processEvents();
954 }
908 955
909 if (_isFinished) {956 if (_isFinished) {
910 break;957 break;
@@ -936,10 +983,10 @@ void ScriptEngine::run() { · 25 unmodified lines
936 _lastUpdate = now;983 _lastUpdate = now;
937 984
938 // Debug and clear exceptions985 // Debug and clear exceptions
939 hadUncaughtExceptions(*this, _fileNameString);986 hadUncaughtExceptions(*this, _fileNameString, this);
940 }987 }
941 988
942 qCDebug(scriptengine) << "Script Engine stopping:" << getFilename();989 scriptInfoMessage("Script Engine stopping:" + getFilename());
943 990
944 stopAllTimers(); // make sure all our timers are stopped if the script is ending991 stopAllTimers(); // make sure all our timers are stopped if the script is ending
945 emit scriptEnding();992 emit scriptEnding();
@@ -967,6 +1014,21 @@ void ScriptEngine::run() { · 21 unmodified lines
967 emit doneRunning();1014 emit doneRunning();
968}1015}
969 1016
1017quint64 ScriptEngine::getTimersRemainingTime() {
1018 quint64 minimumTime = USECS_PER_SECOND; // anything larger than this can be ignored
1019 QMutableHashIterator<QTimer*, CallbackData> i(_timerFunctionMap);
1020 while (i.hasNext()) {
1021 i.next();
1022 QTimer* timer = i.key();
1023 int remainingTime = timer->remainingTime();
1024 if (remainingTime >= 0) {
1025 minimumTime = std::min((quint64)remainingTime, minimumTime);
1026 }
1027 }
1028 return minimumTime;
1029}
1030
1031
970// NOTE: This is private because it must be called on the same thread that created the timers, which is why1032// NOTE: This is private because it must be called on the same thread that created the timers, which is why
971// we want to only call it in our own run "shutdown" processing.1033// we want to only call it in our own run "shutdown" processing.
972void ScriptEngine::stopAllTimers() {1034void ScriptEngine::stopAllTimers() {
@@ -1012,7 +1074,7 @@ void ScriptEngine::stop(bool marshal) { · 39 unmodified lines
1012void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler) {1074void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler) {
1013 if (QThread::currentThread() != thread()) {1075 if (QThread::currentThread() != thread()) {
1014#ifdef THREAD_DEBUGGING1076#ifdef THREAD_DEBUGGING
1015 qDebug() << "*** WARNING *** ScriptEngine::callAnimationStateHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name;1077 qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::callAnimationStateHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name;
1016#endif1078#endif
1017 QMetaObject::invokeMethod(this, "callAnimationStateHandler",1079 QMetaObject::invokeMethod(this, "callAnimationStateHandler",
1018 Q_ARG(QScriptValue, callback),1080 Q_ARG(QScriptValue, callback),
@@ -1046,7 +1108,7 @@ void ScriptEngine::timerFired() { · 27 unmodified lines
1046 {1108 {
1047 auto engine = DependencyManager::get<ScriptEngines>();1109 auto engine = DependencyManager::get<ScriptEngines>();
1048 if (!engine || engine->isStopped()) {1110 if (!engine || engine->isStopped()) {
1049 qCDebug(scriptengine) << "Script.timerFired() while shutting down is ignored... parent script:" << getFilename();1111 scriptWarningMessage("Script.timerFired() while shutting down is ignored... parent script:" + getFilename());
1050 return; // bail early1112 return; // bail early
1051 }1113 }
1052 }1114 }
@@ -1062,7 +1124,12 @@ void ScriptEngine::timerFired() { · 9 unmodified lines
1062 1124
1063 // call the associated JS function, if it exists1125 // call the associated JS function, if it exists
1064 if (timerData.function.isValid()) {1126 if (timerData.function.isValid()) {
1127 auto preTimer = p_high_resolution_clock::now();
1065 callWithEnvironment(timerData.definingEntityIdentifier, timerData.definingSandboxURL, timerData.function, timerData.function, QScriptValueList());1128 callWithEnvironment(timerData.definingEntityIdentifier, timerData.definingSandboxURL, timerData.function, timerData.function, QScriptValueList());
1129 auto postTimer = p_high_resolution_clock::now();
1130 auto elapsed = (postTimer - preTimer);
1131 _totalTimerExecution += std::chrono::duration_cast<std::chrono::microseconds>(elapsed);
1132
1066 }1133 }
1067}1134}
1068 1135
@@ -1072,12 +1139,18 @@ QObject* ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int · 3 unmodified lines
1072 QTimer* newTimer = new QTimer(this);1139 QTimer* newTimer = new QTimer(this);
1073 newTimer->setSingleShot(isSingleShot);1140 newTimer->setSingleShot(isSingleShot);
1074 1141
1142 // The default timer type is not very accurate below about 200ms http://doc.qt.io/qt-5/qt.html#TimerType-enum
1143 static const int MIN_TIMEOUT_FOR_COARSE_TIMER = 200;
1144 if (intervalMS < MIN_TIMEOUT_FOR_COARSE_TIMER) {
1145 newTimer->setTimerType(Qt::PreciseTimer);
1146 }
1147
1075 connect(newTimer, &QTimer::timeout, this, &ScriptEngine::timerFired);1148 connect(newTimer, &QTimer::timeout, this, &ScriptEngine::timerFired);
1076 1149
1077 // make sure the timer stops when the script does1150 // make sure the timer stops when the script does
1078 connect(this, &ScriptEngine::scriptEnding, newTimer, &QTimer::stop);1151 connect(this, &ScriptEngine::scriptEnding, newTimer, &QTimer::stop);
1079 1152
1080 CallbackData timerData = {function, currentEntityIdentifier, currentSandboxURL};1153 CallbackData timerData = {function, currentEntityIdentifier, currentSandboxURL };
1081 _timerFunctionMap.insert(newTimer, timerData);1154 _timerFunctionMap.insert(newTimer, timerData);
1082 1155
1083 newTimer->start(intervalMS);1156 newTimer->start(intervalMS);
@@ -1086,7 +1159,7 @@ QObject* ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int · 2 unmodified lines
1086 1159
1087QObject* ScriptEngine::setInterval(const QScriptValue& function, int intervalMS) {1160QObject* ScriptEngine::setInterval(const QScriptValue& function, int intervalMS) {
1088 if (DependencyManager::get<ScriptEngines>()->isStopped()) {1161 if (DependencyManager::get<ScriptEngines>()->isStopped()) {
1089 qCDebug(scriptengine) << "Script.setInterval() while shutting down is ignored... parent script:" << getFilename();1162 scriptWarningMessage("Script.setInterval() while shutting down is ignored... parent script:" + getFilename());
1090 return NULL; // bail early1163 return NULL; // bail early
1091 }1164 }
1092 1165
@@ -1095,7 +1168,7 @@ QObject* ScriptEngine::setInterval(const QScriptValue& function, int intervalMS) · 2 unmodified lines
1095 1168
1096QObject* ScriptEngine::setTimeout(const QScriptValue& function, int timeoutMS) {1169QObject* ScriptEngine::setTimeout(const QScriptValue& function, int timeoutMS) {
1097 if (DependencyManager::get<ScriptEngines>()->isStopped()) {1170 if (DependencyManager::get<ScriptEngines>()->isStopped()) {
1098 qCDebug(scriptengine) << "Script.setTimeout() while shutting down is ignored... parent script:" << getFilename();1171 scriptWarningMessage("Script.setTimeout() while shutting down is ignored... parent script:" + getFilename());
1099 return NULL; // bail early1172 return NULL; // bail early
1100 }1173 }
1101 1174
@@ -1155,8 +1228,8 @@ void ScriptEngine::print(const QString& message) { · 53 unmodified lines
1155// all of the files have finished loading.1228// all of the files have finished loading.
1156void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) {1229void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) {
1157 if (DependencyManager::get<ScriptEngines>()->isStopped()) {1230 if (DependencyManager::get<ScriptEngines>()->isStopped()) {
1158 qCDebug(scriptengine) << "Script.include() while shutting down is ignored..."1231 scriptWarningMessage("Script.include() while shutting down is ignored... includeFiles:"
1159 << "includeFiles:" << includeFiles << "parent script:" << getFilename();1232 + includeFiles.join(",") + "parent script:" + getFilename());
1160 return; // bail early1233 return; // bail early
1161 }1234 }
1162 QList<QUrl> urls;1235 QList<QUrl> urls;
@@ -1176,14 +1249,15 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac · 13 unmodified lines
1176 // Guard against meaningless query and fragment parts.1249 // Guard against meaningless query and fragment parts.
1177 // Do NOT use PreferLocalFile as its behavior is unpredictable (e.g., on defaultScriptsLocation())1250 // Do NOT use PreferLocalFile as its behavior is unpredictable (e.g., on defaultScriptsLocation())
1178 const auto strippingFlags = QUrl::RemoveFilename | QUrl::RemoveQuery | QUrl::RemoveFragment;1251 const auto strippingFlags = QUrl::RemoveFilename | QUrl::RemoveQuery | QUrl::RemoveFragment;
1179 for (QString file : includeFiles) {1252 for (QString includeFile : includeFiles) {
1253 QString file = ResourceManager::normalizeURL(includeFile);
1180 QUrl thisURL;1254 QUrl thisURL;
1181 bool isStandardLibrary = false;1255 bool isStandardLibrary = false;
1182 if (file.startsWith("/~/")) {1256 if (file.startsWith("/~/")) {
1183 thisURL = expandScriptUrl(QUrl::fromLocalFile(expandScriptPath(file)));1257 thisURL = expandScriptUrl(QUrl::fromLocalFile(expandScriptPath(file)));
1184 QUrl defaultScriptsLoc = defaultScriptsLocation();1258 QUrl defaultScriptsLoc = defaultScriptsLocation();
1185 if (!defaultScriptsLoc.isParentOf(thisURL)) {1259 if (!defaultScriptsLoc.isParentOf(thisURL)) {
1186 qDebug() << "ScriptEngine::include -- skipping" << file << "-- outside of standard libraries";1260 scriptWarningMessage("Script.include() -- skipping" + file + "-- outside of standard libraries");
1187 continue;1261 continue;
1188 }1262 }
1189 isStandardLibrary = true;1263 isStandardLibrary = true;
@@ -1191,20 +1265,16 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac · 1 unmodified line
1191 thisURL = resolvePath(file);1265 thisURL = resolvePath(file);
1192 }1266 }
1193 1267
1194 if (!_includedURLs.contains(thisURL)) {1268 if (!isStandardLibrary && !currentSandboxURL.isEmpty() && (thisURL.scheme() == "file") &&
1195 if (!isStandardLibrary && !currentSandboxURL.isEmpty() && (thisURL.scheme() == "file") &&1269 (currentSandboxURL.scheme() != "file" ||
1196 (currentSandboxURL.scheme() != "file" ||1270 !thisURL.toString(strippingFlags).startsWith(currentSandboxURL.toString(strippingFlags), getSensitivity()))) {
1197 !thisURL.toString(strippingFlags).startsWith(currentSandboxURL.toString(strippingFlags), getSensitivity()))) {1271
1198 qCWarning(scriptengine) << "Script.include() ignoring file path"1272 scriptWarningMessage("Script.include() ignoring file path" + thisURL.toString()
1199 << thisURL << "outside of original entity script" << currentSandboxURL;1273 + "outside of original entity script" + currentSandboxURL.toString());
1200 } else {
1201 // We could also check here for CORS, but we don't yet.
1202 // It turns out that QUrl.resolve will not change hosts and copy authority, so we don't need to check that here.
1203 urls.append(thisURL);
1204 _includedURLs << thisURL;
1205 }
1206 } else {1274 } else {
1207 qCDebug(scriptengine) << "Script.include() ignoring previously included url:" << thisURL;1275 // We could also check here for CORS, but we don't yet.
1276 // It turns out that QUrl.resolve will not change hosts and copy authority, so we don't need to check that here.
1277 urls.append(thisURL);
1208 }1278 }
1209 }1279 }
1210 1280
@@ -1222,15 +1292,22 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac · 11 unmodified lines
1222 for (QUrl url : urls) {1292 for (QUrl url : urls) {
1223 QString contents = data[url];1293 QString contents = data[url];
1224 if (contents.isNull()) {1294 if (contents.isNull()) {
1225 qCDebug(scriptengine) << "Error loading file: " << url << "line:" << __LINE__;1295 scriptErrorMessage("Error loading file: " + url.toString());
1226 } else {1296 } else {
1227 // Set the parent url so that path resolution will be relative1297 std::lock_guard<std::recursive_mutex> lock(_lock);
1228 // to this script's url during its initial evaluation1298 if (!_includedURLs.contains(url)) {
1229 _parentURL = url.toString();1299 _includedURLs << url;
1230 auto operation = [&]() {1300 // Set the parent url so that path resolution will be relative
1231 evaluate(contents, url.toString());1301 // to this script's url during its initial evaluation
1232 };1302 _parentURL = url.toString();
1233 doWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, operation);1303 auto operation = [&]() {
1304 evaluate(contents, url.toString());
1305 };
1306
1307 doWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, operation);
1308 } else {
1309 scriptWarningMessage("Script.include() skipping evaluation of previously included url:" + url.toString());
1310 }
1234 }1311 }
1235 }1312 }
1236 _parentURL = parentURL;1313 _parentURL = parentURL;
@@ -1258,8 +1335,8 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac · 21 unmodified lines
1258 1335
1259void ScriptEngine::include(const QString& includeFile, QScriptValue callback) {1336void ScriptEngine::include(const QString& includeFile, QScriptValue callback) {
1260 if (DependencyManager::get<ScriptEngines>()->isStopped()) {1337 if (DependencyManager::get<ScriptEngines>()->isStopped()) {
1261 qCDebug(scriptengine) << "Script.include() while shutting down is ignored... "1338 scriptWarningMessage("Script.include() while shutting down is ignored... includeFile:"
1262 << "includeFile:" << includeFile << "parent script:" << getFilename();1339 + includeFile + "parent script:" + getFilename());
1263 return; // bail early1340 return; // bail early
1264 }1341 }
1265 1342
@@ -1273,13 +1350,13 @@ void ScriptEngine::include(const QString& includeFile, QScriptValue callback) { · 7 unmodified lines
1273// the Application or other context will connect to in order to know to actually load the script1350// the Application or other context will connect to in order to know to actually load the script
1274void ScriptEngine::load(const QString& loadFile) {1351void ScriptEngine::load(const QString& loadFile) {
1275 if (DependencyManager::get<ScriptEngines>()->isStopped()) {1352 if (DependencyManager::get<ScriptEngines>()->isStopped()) {
1276 qCDebug(scriptengine) << "Script.load() while shutting down is ignored... "1353 scriptWarningMessage("Script.load() while shutting down is ignored... loadFile:"
1277 << "loadFile:" << loadFile << "parent script:" << getFilename();1354 + loadFile + "parent script:" + getFilename());
1278 return; // bail early1355 return; // bail early
1279 }1356 }
1280 if (!currentEntityIdentifier.isInvalidID()) {1357 if (!currentEntityIdentifier.isInvalidID()) {
1281 qCWarning(scriptengine) << "Script.load() from entity script is ignored... "1358 scriptWarningMessage("Script.load() from entity script is ignored... loadFile:"
1282 << "loadFile:" << loadFile << "parent script:" << getFilename();1359 + loadFile + "parent script:" + getFilename());
1283 return; // bail early1360 return; // bail early
1284 }1361 }
1285 1362
@@ -1296,7 +1373,7 @@ void ScriptEngine::load(const QString& loadFile) { · 10 unmodified lines
1296// Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args1373// Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args
1297void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHandlerArgs) {1374void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHandlerArgs) {
1298 if (QThread::currentThread() != thread()) {1375 if (QThread::currentThread() != thread()) {
1299 qDebug() << "*** ERROR *** ScriptEngine::forwardHandlerCall() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";1376 qCDebug(scriptengine) << "*** ERROR *** ScriptEngine::forwardHandlerCall() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";
1300 assert(false);1377 assert(false);
1301 return ;1378 return ;
1302 }1379 }
@@ -1330,7 +1407,7 @@ void ScriptEngine::loadEntityScript(QWeakPointer<ScriptEngine> theEngine, const · 27 unmodified lines
1330 QSharedPointer<ScriptEngine> strongEngine = theEngine.toStrongRef();1407 QSharedPointer<ScriptEngine> strongEngine = theEngine.toStrongRef();
1331 if (strongEngine) {1408 if (strongEngine) {
1332#ifdef THREAD_DEBUGGING1409#ifdef THREAD_DEBUGGING
1333 qDebug() << "ScriptEngine::entityScriptContentAvailable() IN LAMBDA contentAvailable on thread ["1410 qCDebug(scriptengine) << "ScriptEngine::entityScriptContentAvailable() IN LAMBDA contentAvailable on thread ["
1334 << QThread::currentThread() << "] expected thread [" << strongEngine->thread() << "]";1411 << QThread::currentThread() << "] expected thread [" << strongEngine->thread() << "]";
1335#endif1412#endif
1336 strongEngine->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success);1413 strongEngine->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success);
@@ -1343,7 +1420,7 @@ void ScriptEngine::loadEntityScript(QWeakPointer<ScriptEngine> theEngine, const · 6 unmodified lines
1343void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success) {1420void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success) {
1344 if (QThread::currentThread() != thread()) {1421 if (QThread::currentThread() != thread()) {
1345#ifdef THREAD_DEBUGGING1422#ifdef THREAD_DEBUGGING
1346 qDebug() << "*** WARNING *** ScriptEngine::entityScriptContentAvailable() called on wrong thread ["1423 qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::entityScriptContentAvailable() called on wrong thread ["
1347 << QThread::currentThread() << "], invoking on correct thread [" << thread()1424 << QThread::currentThread() << "], invoking on correct thread [" << thread()
1348 << "] " "entityID:" << entityID << "scriptOrURL:" << scriptOrURL << "contents:"1425 << "] " "entityID:" << entityID << "scriptOrURL:" << scriptOrURL << "contents:"
1349 << contents << "isURL:" << isURL << "success:" << success;1426 << contents << "isURL:" << isURL << "success:" << success;
@@ -1359,7 +1436,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 9 unmodified lines
1359 }1436 }
1360 1437
1361#ifdef THREAD_DEBUGGING1438#ifdef THREAD_DEBUGGING
1362 qDebug() << "ScriptEngine::entityScriptContentAvailable() thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]";1439 qCDebug(scriptengine) << "ScriptEngine::entityScriptContentAvailable() thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]";
1363#endif1440#endif
1364 1441
1365 auto scriptCache = DependencyManager::get<ScriptCache>();1442 auto scriptCache = DependencyManager::get<ScriptCache>();
@@ -1367,7 +1444,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 1 unmodified line
1367 auto fileName = isURL ? scriptOrURL : "EmbeddedEntityScript";1444 auto fileName = isURL ? scriptOrURL : "EmbeddedEntityScript";
1368 1445
1369 QScriptProgram program(contents, fileName);1446 QScriptProgram program(contents, fileName);
1370 if (!hasCorrectSyntax(program)) {1447 if (!hasCorrectSyntax(program, this)) {
1371 if (!isFileUrl) {1448 if (!isFileUrl) {
1372 scriptCache->addScriptToBadScriptList(scriptOrURL);1449 scriptCache->addScriptToBadScriptList(scriptOrURL);
1373 }1450 }
@@ -1395,7 +1472,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 21 unmodified lines
1395 });1472 });
1396 testConstructor = sandbox.evaluate(program);1473 testConstructor = sandbox.evaluate(program);
1397 }1474 }
1398 if (hadUncaughtExceptions(sandbox, program.fileName())) {1475 if (hadUncaughtExceptions(sandbox, program.fileName(), this)) {
1399 return;1476 return;
1400 }1477 }
1401 1478
@@ -1409,10 +1486,10 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 7 unmodified lines
1409 if (testConstructorValue.size() > maxTestConstructorValueSize) {1486 if (testConstructorValue.size() > maxTestConstructorValueSize) {
1410 testConstructorValue = testConstructorValue.mid(0, maxTestConstructorValueSize) + "...";1487 testConstructorValue = testConstructorValue.mid(0, maxTestConstructorValueSize) + "...";
1411 }1488 }
1412 qCDebug(scriptengine) << "Error -- ScriptEngine::loadEntityScript() entity:" << entityID1489 scriptErrorMessage("Error -- ScriptEngine::loadEntityScript() entity:" + entityID.toString()
1413 << "failed to load entity script -- expected a function, got " + testConstructorType1490 + "failed to load entity script -- expected a function, got " + testConstructorType
1414 << "," << testConstructorValue1491 + "," + testConstructorValue
1415 << "," << scriptOrURL;1492 + "," + scriptOrURL);
1416 1493
1417 if (!isFileUrl) {1494 if (!isFileUrl) {
1418 scriptCache->addScriptToBadScriptList(scriptOrURL);1495 scriptCache->addScriptToBadScriptList(scriptOrURL);
@@ -1447,7 +1524,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 28 unmodified lines
1447void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) {1524void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) {
1448 if (QThread::currentThread() != thread()) {1525 if (QThread::currentThread() != thread()) {
1449#ifdef THREAD_DEBUGGING1526#ifdef THREAD_DEBUGGING
1450 qDebug() << "*** WARNING *** ScriptEngine::unloadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "1527 qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::unloadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
1451 "entityID:" << entityID;1528 "entityID:" << entityID;
1452#endif1529#endif
1453 1530
@@ -1456,7 +1533,7 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) { · 2 unmodified lines
1456 return;1533 return;
1457 }1534 }
1458#ifdef THREAD_DEBUGGING1535#ifdef THREAD_DEBUGGING
1459 qDebug() << "ScriptEngine::unloadEntityScript() called on correct thread [" << thread() << "] "1536 qCDebug(scriptengine) << "ScriptEngine::unloadEntityScript() called on correct thread [" << thread() << "] "
1460 "entityID:" << entityID;1537 "entityID:" << entityID;
1461#endif1538#endif
1462 1539
@@ -1470,14 +1547,14 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) { · 7 unmodified lines
1470void ScriptEngine::unloadAllEntityScripts() {1547void ScriptEngine::unloadAllEntityScripts() {
1471 if (QThread::currentThread() != thread()) {1548 if (QThread::currentThread() != thread()) {
1472#ifdef THREAD_DEBUGGING1549#ifdef THREAD_DEBUGGING
1473 qDebug() << "*** WARNING *** ScriptEngine::unloadAllEntityScripts() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";1550 qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::unloadAllEntityScripts() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";
1474#endif1551#endif
1475 1552
1476 QMetaObject::invokeMethod(this, "unloadAllEntityScripts");1553 QMetaObject::invokeMethod(this, "unloadAllEntityScripts");
1477 return;1554 return;
1478 }1555 }
1479#ifdef THREAD_DEBUGGING1556#ifdef THREAD_DEBUGGING
1480 qDebug() << "ScriptEngine::unloadAllEntityScripts() called on correct thread [" << thread() << "]";1557 qCDebug(scriptengine) << "ScriptEngine::unloadAllEntityScripts() called on correct thread [" << thread() << "]";
1481#endif1558#endif
1482 foreach(const EntityItemID& entityID, _entityScripts.keys()) {1559 foreach(const EntityItemID& entityID, _entityScripts.keys()) {
1483 callEntityScriptMethod(entityID, "unload");1560 callEntityScriptMethod(entityID, "unload");
@@ -1485,13 +1562,13 @@ void ScriptEngine::unloadAllEntityScripts() { · 1 unmodified line
1485 _entityScripts.clear();1562 _entityScripts.clear();
1486 1563
1487#ifdef DEBUG_ENGINE_STATE1564#ifdef DEBUG_ENGINE_STATE
1488 qDebug() << "---- CURRENT STATE OF ENGINE: --------------------------";1565 qCDebug(scriptengine) << "---- CURRENT STATE OF ENGINE: --------------------------";
1489 QScriptValueIterator it(globalObject());1566 QScriptValueIterator it(globalObject());
1490 while (it.hasNext()) {1567 while (it.hasNext()) {
1491 it.next();1568 it.next();
1492 qDebug() << it.name() << ":" << it.value().toString();1569 qCDebug(scriptengine) << it.name() << ":" << it.value().toString();
1493 }1570 }
1494 qDebug() << "--------------------------------------------------------";1571 qCDebug(scriptengine) << "--------------------------------------------------------";
1495#endif // DEBUG_ENGINE_STATE1572#endif // DEBUG_ENGINE_STATE
1496}1573}
1497 1574
@@ -1512,7 +1589,7 @@ void ScriptEngine::refreshFileScript(const EntityItemID& entityID) { · 14 unmodified lines
1512 QString filePath = QUrl(details.scriptText).toLocalFile();1589 QString filePath = QUrl(details.scriptText).toLocalFile();
1513 auto lastModified = QFileInfo(filePath).lastModified().toMSecsSinceEpoch();1590 auto lastModified = QFileInfo(filePath).lastModified().toMSecsSinceEpoch();
1514 if (lastModified > details.lastModified) {1591 if (lastModified > details.lastModified) {
1515 qCDebug(scriptengine) << "Reloading modified script " << details.scriptText;1592 scriptInfoMessage("Reloading modified script " + details.scriptText);
1516 1593
1517 QFile file(filePath);1594 QFile file(filePath);
1518 file.open(QIODevice::ReadOnly);1595 file.open(QIODevice::ReadOnly);
@@ -1520,7 +1597,7 @@ void ScriptEngine::refreshFileScript(const EntityItemID& entityID) { · 1 unmodified line
1520 this->unloadEntityScript(entityID);1597 this->unloadEntityScript(entityID);
1521 this->entityScriptContentAvailable(entityID, details.scriptText, scriptContents, true, true);1598 this->entityScriptContentAvailable(entityID, details.scriptText, scriptContents, true, true);
1522 if (!_entityScripts.contains(entityID)) {1599 if (!_entityScripts.contains(entityID)) {
1523 qWarning() << "Reload script " << details.scriptText << " failed";1600 scriptWarningMessage("Reload script " + details.scriptText + " failed");
1524 } else {1601 } else {
1525 details = _entityScripts[entityID];1602 details = _entityScripts[entityID];
1526 }1603 }
@@ -1547,7 +1624,7 @@ void ScriptEngine::doWithEnvironment(const EntityItemID& entityID, const QUrl& s · 20 unmodified lines
1547#else1624#else
1548 operation();1625 operation();
1549#endif1626#endif
1550 hadUncaughtExceptions(*this, _fileNameString);1627 hadUncaughtExceptions(*this, _fileNameString, this);
1551 1628
1552 currentEntityIdentifier = oldIdentifier;1629 currentEntityIdentifier = oldIdentifier;
1553 currentSandboxURL = oldSandboxURL;1630 currentSandboxURL = oldSandboxURL;
@@ -1562,7 +1639,7 @@ void ScriptEngine::callWithEnvironment(const EntityItemID& entityID, const QUrl& · 8 unmodified lines
1562void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params) {1639void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params) {
1563 if (QThread::currentThread() != thread()) {1640 if (QThread::currentThread() != thread()) {
1564#ifdef THREAD_DEBUGGING1641#ifdef THREAD_DEBUGGING
1565 qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "1642 qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
1566 "entityID:" << entityID << "methodName:" << methodName;1643 "entityID:" << entityID << "methodName:" << methodName;
1567#endif1644#endif
1568 1645
@@ -1573,7 +1650,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 4 unmodified lines
1573 return;1650 return;
1574 }1651 }
1575#ifdef THREAD_DEBUGGING1652#ifdef THREAD_DEBUGGING
1576 qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] "1653 qCDebug(scriptengine) << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] "
1577 "entityID:" << entityID << "methodName:" << methodName;1654 "entityID:" << entityID << "methodName:" << methodName;
1578#endif1655#endif
1579 1656
@@ -1594,7 +1671,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 14 unmodified lines
1594void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event) {1671void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event) {
1595 if (QThread::currentThread() != thread()) {1672 if (QThread::currentThread() != thread()) {
1596#ifdef THREAD_DEBUGGING1673#ifdef THREAD_DEBUGGING
1597 qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "1674 qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
1598 "entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent";1675 "entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent";
1599#endif1676#endif
1600 1677
@@ -1605,7 +1682,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 4 unmodified lines
1605 return;1682 return;
1606 }1683 }
1607#ifdef THREAD_DEBUGGING1684#ifdef THREAD_DEBUGGING
1608 qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] "1685 qCDebug(scriptengine) << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] "
1609 "entityID:" << entityID << "methodName:" << methodName << "event: pointerEvent";1686 "entityID:" << entityID << "methodName:" << methodName << "event: pointerEvent";
1610#endif1687#endif
1611 1688
@@ -1626,7 +1703,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 14 unmodified lines
1626void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision) {1703void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision) {
1627 if (QThread::currentThread() != thread()) {1704 if (QThread::currentThread() != thread()) {
1628#ifdef THREAD_DEBUGGING1705#ifdef THREAD_DEBUGGING
1629 qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "1706 qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
1630 "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";1707 "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";
1631#endif1708#endif
1632 1709
@@ -1638,7 +1715,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 5 unmodified lines
1638 return;1715 return;
1639 }1716 }
1640#ifdef THREAD_DEBUGGING1717#ifdef THREAD_DEBUGGING
1641 qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] "1718 qCDebug(scriptengine) << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] "
1642 "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";1719 "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";
1643#endif1720#endif
1644 1721