History

* refactor ScriptCache per FIXMEs * include error details in BatchLoader results * update EntityServerScript properties in edtior to reflect more granular statuses * ScriptEngine plumbing in prep for require/module integration

humbletim committed 9 years ago

Commit 831bf931 parent e6142d914 files · +286 −257Browse files

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

Unified · Split

libraries/script-engine/src/ScriptEngine.cpp+156−94
@@ -68,10 +68,12 @@ · 67 unmodified lines
6868
6969 #include "MIDIEvent.h"
7070
71−static const QString SCRIPT_EXCEPTION_FORMAT = "[UncaughtException] %1 in %2:%3";
71+const QString BaseScriptEngine::SCRIPT_EXCEPTION_FORMAT { "[UncaughtException] %1 in %2:%3" };
7272 static const QScriptEngine::QObjectWrapOptions DEFAULT_QOBJECT_WRAP_OPTIONS =
7373 QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects;
7474
75+static const bool HIFI_AUTOREFRESH_FILE_SCRIPTS { true };
76+
7577 Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature)
7678 int functionSignatureMetaID = qRegisterMetaType<QScriptEngine::FunctionSignature>();
7779
@@ -137,38 +139,48 @@ QString encodeEntityIdIntoEntityUrl(const QString& url, const QString& entityID) · 59 unmodified lines
137139 return url + " [EntityID:" + entityID + "]";
138140 }
139141
140−static bool hasCorrectSyntax(const QScriptProgram& program, ScriptEngine* reportingEngine) {
141− const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode());
142− if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
142+QString BaseScriptEngine::lintScript(const QString& sourceCode, const QString& fileName, const int lineNumber) {
143+ const auto syntaxCheck = checkSyntax(sourceCode);
144+ if (syntaxCheck.state() != syntaxCheck.Valid) {
143145 const auto error = syntaxCheck.errorMessage();
144146 const auto line = QString::number(syntaxCheck.errorLineNumber());
145147 const auto column = QString::number(syntaxCheck.errorColumnNumber());
146− const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, program.fileName(), line, column);
147− reportingEngine->scriptErrorMessage(qPrintable(message));
148− return false;
149− }
150− return true;
151−}
152−
153−static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName, ScriptEngine* reportingEngine, QString* exceptionMessage = nullptr) {
154− if (engine.hasUncaughtException()) {
155− const auto backtrace = engine.uncaughtExceptionBacktrace();
156− const auto exception = engine.uncaughtException().toString();
157− const auto line = QString::number(engine.uncaughtExceptionLineNumber());
158− engine.clearExceptions();
148+ const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, fileName, line, column);
149+ return message;
150+ }
151+ return QString();
152+}
153+
154+QString BaseScriptEngine::formatUncaughtException(const QString& overrideFileName) {
155+ QString message;
156+ if (hasUncaughtException()) {
157+ const auto error = uncaughtException();
158+ const auto backtrace = uncaughtExceptionBacktrace();
159+ const auto exception = error.toString();
160+ auto filename = overrideFileName;
161+ if (filename.isEmpty()) {
162+ QScriptContextInfo ctx { currentContext() };
163+ filename = ctx.fileName();
164+ }
165+ const auto line = QString::number(uncaughtExceptionLineNumber());
159166
160− QString message = QString(SCRIPT_EXCEPTION_FORMAT).arg(exception, fileName, line);
167+ message = QString(SCRIPT_EXCEPTION_FORMAT).arg(exception, overrideFileName, line);
161168 if (!backtrace.empty()) {
162169 static const auto lineSeparator = "\n ";
163170 message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator));
164171 }
165− reportingEngine->scriptErrorMessage(qPrintable(message));
166− if (exceptionMessage) {
167− *exceptionMessage = message;
168− }
169− return true;
170172 }
171− return false;
173+ return message;
174+}
175+
176+QString ScriptEngine::reportUncaughtException(const QString& overrideFileName) {
177+ QString message;
178+ if (!hasUncaughtException()) {
179+ return message;
180+ }
181+ message = formatUncaughtException(overrideFileName.isEmpty() ? _fileNameString : overrideFileName);
182+ scriptErrorMessage(qPrintable(message));
183+ return message;
172184 }
173185
174186 ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const QString& fileNameString) :
@@ -181,7 +193,8 @@ ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const · 6 unmodified lines
181193 DependencyManager::get<ScriptEngines>()->addScriptEngine(this);
182194
183195 connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) {
184− hadUncaughtExceptions(*this, _fileNameString, this);
196+ reportUncaughtException();
197+ clearExceptions();
185198 });
186199
187200 setProcessEventsInterval(MSECS_PER_SECOND);
@@ -301,7 +314,10 @@ void ScriptEngine::runDebuggable() { · 113 unmodified lines
301314 }
302315 _lastUpdate = now;
303316 // Debug and clear exceptions
304− hadUncaughtExceptions(*this, _fileNameString, this);
317+ if (hasUncaughtException()) {
318+ reportUncaughtException();
319+ clearExceptions();
320+ }
305321 });
306322
307323 timer->start(10);
@@ -399,8 +415,6 @@ QString ScriptEngine::getFilename() const { · 91 unmodified lines
399415 return lastPart;
400416 }
401417
402−
403−// FIXME - switch this to the new model of ScriptCache callbacks
404418 void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) {
405419 if (_isRunning) {
406420 return;
@@ -410,19 +424,27 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) { · 3 unmodified lines
410424 _fileNameString = url.toString();
411425 _isReloading = reload;
412426
413− bool isPending;
427+ const auto maxRetries = 0; // for consistency with previous scriptCache->getScript() behavior
414428 auto scriptCache = DependencyManager::get<ScriptCache>();
415− scriptCache->getScript(url, this, isPending, reload);
416−}
429+ scriptCache->getScriptContents(url.toString(), [this](const QString& url, const QString& scriptContents, bool isURL, bool success, const QString&status) {
430+ qCDebug(scriptengine) << "loadURL" << url << status << QThread::currentThread();
431+ if (!success) {
432+ scriptErrorMessage("ERROR Loading file (" + status + "):" + url);
433+ emit errorLoadingScript(_fileNameString);
434+ return;
435+ }
417436
418−// FIXME - switch this to the new model of ScriptCache callbacks
419−void ScriptEngine::scriptContentsAvailable(const QUrl& url, const QString& scriptContents) {
420− _scriptContents = scriptContents;
421− static const QString DEBUG_FLAG("#debug");
422− if (QRegularExpression(DEBUG_FLAG).match(scriptContents).hasMatch()) {
423− _debuggable = true;
424− }
425− emit scriptLoaded(url.toString());
437+ _scriptContents = scriptContents;
438+
439+ {
440+ static const QString DEBUG_FLAG("#debug");
441+ if (QRegularExpression(DEBUG_FLAG).match(scriptContents).hasMatch()) {
442+ qCWarning(scriptengine) << "NOTE: ScriptEngine for " << QUrl(url).fileName() << " will be launched in debug mode";
443+ _debuggable = true;
444+ }
445+ }
446+ emit scriptLoaded(url);
447+ }, reload, maxRetries);
426448 }
427449
428450 void ScriptEngine::scriptErrorMessage(const QString& message) {
@@ -440,12 +462,6 @@ void ScriptEngine::scriptInfoMessage(const QString& message) { · 11 unmodified lines
440462 emit infoMessage(message);
441463 }
442464
443−// FIXME - switch this to the new model of ScriptCache callbacks
444−void ScriptEngine::errorInLoadingScript(const QUrl& url) {
445− scriptErrorMessage("ERROR Loading file:" + url.toString());
446− emit errorLoadingScript(_fileNameString);
447−}
448−
449465 // Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of
450466 // callAnimationStateHandler requires that the type be registered.
451467 // These two are meaningful, if we ever do want to use them...
@@ -520,6 +536,15 @@ void ScriptEngine::init() { · 68 unmodified lines
520536
521537 auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
522538 entityScriptingInterface->init();
539+ connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, this, [this](const EntityItemID& entityID) {
540+ if (_entityScripts.contains(entityID)) {
541+ if (isEntityScriptRunning(entityID)) {
542+ qCWarning(scriptengine) << "deletingEntity while entity script is still running!" << entityID;
543+ }
544+ _entityScripts.remove(entityID);
545+ }
546+ });
547+
523548
524549 // register various meta-types
525550 registerMetaTypes(this);
@@ -850,17 +875,25 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi · 324 unmodified lines
850875 }
851876
852877 // Check syntax
853− const QScriptProgram program(sourceCode, fileName, lineNumber);
854− if (!hasCorrectSyntax(program, this)) {
878+ auto syntaxError = lintScript(sourceCode, fileName);
879+ QScriptProgram program { sourceCode, fileName, lineNumber };
880+ if (!syntaxError.isEmpty() || program.isNull()) {
881+ scriptErrorMessage(qPrintable(syntaxError));
855882 return QScriptValue();
856883 }
857884
858885 ++_evaluatesPending;
859− const auto result = QScriptEngine::evaluate(program);
886+ auto result = BaseScriptEngine::evaluate(program);
860887 --_evaluatesPending;
861888
862− const auto hadUncaughtException = hadUncaughtExceptions(*this, program.fileName(), this);
863− emit evaluationFinished(result, hadUncaughtException);
889+ if (hasUncaughtException()) {
890+ result = uncaughtException();
891+ reportUncaughtException(program.fileName());
892+ emit evaluationFinished(result, true);
893+ clearExceptions();
894+ } else {
895+ emit evaluationFinished(result, false);
896+ }
864897 return result;
865898 }
866899
@@ -1009,7 +1042,10 @@ void ScriptEngine::run() { · 142 unmodified lines
10091042 _lastUpdate = now;
10101043
10111044 // Debug and clear exceptions
1012− hadUncaughtExceptions(*this, _fileNameString, this);
1045+ if (hasUncaughtException()) {
1046+ reportUncaughtException();
1047+ clearExceptions();
1048+ }
10131049 }
10141050
10151051 scriptInfoMessage("Script Engine stopping:" + getFilename());
@@ -1299,12 +1335,12 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac · 283 unmodified lines
12991335 EntityItemID capturedEntityIdentifier = currentEntityIdentifier;
13001336 QUrl capturedSandboxURL = currentSandboxURL;
13011337
1302− auto evaluateScripts = [=](const QMap<QUrl, QString>& data) {
1338+ auto evaluateScripts = [=](const QMap<QUrl, QString>& data, const QMap<QUrl, QString>& status) {
13031339 auto parentURL = _parentURL;
13041340 for (QUrl url : urls) {
13051341 QString contents = data[url];
13061342 if (contents.isNull()) {
1307− scriptErrorMessage("Error loading file: " + url.toString());
1343+ scriptErrorMessage("Error loading file (" + status[url] +"): " + url.toString());
13081344 } else {
13091345 std::lock_guard<std::recursive_mutex> lock(_lock);
13101346 if (!_includedURLs.contains(url)) {
@@ -1368,7 +1404,7 @@ void ScriptEngine::load(const QString& loadFile) { · 57 unmodified lines
13681404 }
13691405 if (!currentEntityIdentifier.isInvalidID()) {
13701406 scriptWarningMessage("Script.load() from entity script is ignored... loadFile:"
1371− + loadFile + "parent script:" + getFilename());
1407+ + loadFile + "parent script:" + getFilename() + "entity: " + currentEntityIdentifier.toString());
13721408 return; // bail early
13731409 }
13741410
@@ -1411,11 +1447,16 @@ void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QStrin · 36 unmodified lines
14111447 int ScriptEngine::getNumRunningEntityScripts() const {
14121448 int sum = 0;
14131449 for (auto& st : _entityScripts) {
1414− if (st.status == RUNNING) {
1450+ if (st.status == EntityScriptStatus::RUNNING) {
14151451 ++sum;
14161452 }
14171453 }
14181454 return sum;
1455+
1456+QString ScriptEngine::getEntityScriptStatus(const EntityItemID& entityID) {
1457+ if (_entityScripts.contains(entityID))
1458+ return EntityScriptStatus_::valueToKey(_entityScripts[entityID].status).toLower();
1459+ return QString();
14191460 }
14201461
14211462 bool ScriptEngine::getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const {
@@ -1430,25 +1471,37 @@ bool ScriptEngine::getEntityScriptDetails(const EntityItemID& entityID, EntitySc · 8 unmodified lines
14301471 // since all of these operations can be asynch we will always do the actual work in the response handler
14311472 // for the download
14321473 void ScriptEngine::loadEntityScript(QWeakPointer<ScriptEngine> theEngine, const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {
1474+ {
1475+ // set EntityScriptDetails.status to LOADING (over on theEngine's thread)
1476+ QObject threadPunter;
1477+ connect(&threadPunter, &QObject::destroyed, theEngine.data(), [=]() {
1478+ EntityScriptDetails &details = theEngine.data()->_entityScripts[entityID];
1479+ if (details.status == EntityScriptStatus::PENDING || details.status == EntityScriptStatus::UNLOADED) {
1480+ details.status = EntityScriptStatus::LOADING;
1481+ emit entityScriptDetailsUpdated();
1482+ }
1483+ });
1484+ }
1485+
14331486 // NOTE: If the script content is not currently in the cache, the LAMBDA here will be called on the Main Thread
14341487 // which means we're guaranteed that it's not the correct thread for the ScriptEngine. This means
14351488 // when we get into entityScriptContentAvailable() we will likely invokeMethod() to get it over
14361489 // to the "Entities" ScriptEngine thread.
1437− DependencyManager::get<ScriptCache>()->getScriptContents(entityScript, [theEngine, entityID](const QString& scriptOrURL, const QString& contents, bool isURL, bool success) {
1490+ DependencyManager::get<ScriptCache>()->getScriptContents(entityScript, [theEngine, entityID](const QString& scriptOrURL, const QString& contents, bool isURL, bool success, const QString &status) {
14381491 QSharedPointer<ScriptEngine> strongEngine = theEngine.toStrongRef();
14391492 if (strongEngine) {
14401493 #ifdef THREAD_DEBUGGING
14411494 qCDebug(scriptengine) << "ScriptEngine::entityScriptContentAvailable() IN LAMBDA contentAvailable on thread ["
14421495 << QThread::currentThread() << "] expected thread [" << strongEngine->thread() << "]";
14431496 #endif
1444− strongEngine->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success);
1497+ strongEngine->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success, status);
14451498 }
14461499 }, forceRedownload);
14471500 }
14481501
14491502 // since all of these operations can be asynch we will always do the actual work in the response handler
14501503 // for the download
1451−void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success) {
1504+void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success , const QString& status) {
14521505 if (QThread::currentThread() != thread()) {
14531506 #ifdef THREAD_DEBUGGING
14541507 qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::entityScriptContentAvailable() called on wrong thread ["
@@ -1462,7 +1515,8 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 7 unmodified lines
14621515 Q_ARG(const QString&, scriptOrURL),
14631516 Q_ARG(const QString&, contents),
14641517 Q_ARG(bool, isURL),
1465− Q_ARG(bool, success));
1518+ Q_ARG(bool, success),
1519+ Q_ARG(const QString&, status));
14661520 return;
14671521 }
14681522
@@ -1478,22 +1532,21 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 9 unmodified lines
14781532 newDetails.scriptText = scriptOrURL;
14791533
14801534 if (!success) {
1481− newDetails.status = ERROR_LOADING_SCRIPT;
1482− newDetails.errorInfo = "Failed to load script";
1535+ newDetails.status = EntityScriptStatus::ERROR_LOADING_SCRIPT;
1536+ newDetails.errorInfo = "Failed to load script (" + status + ")";
14831537 _entityScripts[entityID] = newDetails;
14841538 emit entityScriptDetailsUpdated();
14851539 return;
14861540 }
14871541
1488− QScriptProgram program(contents, fileName);
1489− if (!hasCorrectSyntax(program, this)) {
1490− if (!isFileUrl) {
1491− scriptCache->addScriptToBadScriptList(scriptOrURL);
1492− }
1493− newDetails.status = ERROR_RUNNING_SCRIPT;
1494− newDetails.errorInfo = "Bad syntax";
1542+ auto syntaxError = lintScript(contents, fileName);
1543+ QScriptProgram program { contents, fileName };
1544+ if (!syntaxError.isNull() || program.isNull()) {
1545+ newDetails.status = EntityScriptStatus::ERROR_RUNNING_SCRIPT;
1546+ newDetails.errorInfo = QString("Bad syntax (%1)").arg(syntaxError);
14951547 _entityScripts[entityID] = newDetails;
14961548 emit entityScriptDetailsUpdated();
1549+ qCDebug(scriptengine) << newDetails.errorInfo << scriptOrURL;
14971550 return; // done processing script
14981551 }
14991552
@@ -1502,16 +1555,18 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 2 unmodified lines
15021555 }
15031556
15041557 const int SANDBOX_TIMEOUT = 0.25 * MSECS_PER_SECOND;
1505− QScriptEngine sandbox;
1558+ BaseScriptEngine sandbox;
15061559 sandbox.setProcessEventsInterval(SANDBOX_TIMEOUT);
15071560 QScriptValue testConstructor;
15081561 {
15091562 QTimer timeout;
15101563 timeout.setSingleShot(true);
15111564 timeout.start(SANDBOX_TIMEOUT);
1512− connect(&timeout, &QTimer::timeout, [&sandbox, SANDBOX_TIMEOUT]{
1565+ connect(&timeout, &QTimer::timeout, [&sandbox, SANDBOX_TIMEOUT, scriptOrURL]{
15131566 auto context = sandbox.currentContext();
15141567 if (context) {
1568+ qCDebug(scriptengine) << "ScriptEngine::entityScriptContentAvailable timeout(" << scriptOrURL << ")";
1569+
15151570 // Guard against infinite loops and non-performant code
15161571 context->throwError(QString("Timed out (entity constructors are limited to %1ms)").arg(SANDBOX_TIMEOUT));
15171572 }
@@ -1519,13 +1574,13 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 1 unmodified line
15191574 testConstructor = sandbox.evaluate(program);
15201575 }
15211576
1522− QString exceptionMessage;
1523− if (hadUncaughtExceptions(sandbox, program.fileName(), this, &exceptionMessage)) {
1524− newDetails.status = ERROR_RUNNING_SCRIPT;
1577+ QString exceptionMessage = sandbox.formatUncaughtException(program.fileName());
1578+ if (!exceptionMessage.isNull()) {
1579+ newDetails.status = EntityScriptStatus::ERROR_RUNNING_SCRIPT;
15251580 newDetails.errorInfo = exceptionMessage;
15261581 _entityScripts[entityID] = newDetails;
15271582 emit entityScriptDetailsUpdated();
1528−
1583+ qCDebug(scriptengine) << "----- ScriptEngine::entityScriptContentAvailable -- hadUncaughtExceptions (" << scriptOrURL << ")";
15291584 return;
15301585 }
15311586
@@ -1544,15 +1599,12 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 12 unmodified lines
15441599 + "," + testConstructorValue
15451600 + "," + scriptOrURL);
15461601
1547− if (!isFileUrl) {
1548− scriptCache->addScriptToBadScriptList(scriptOrURL);
1549− }
1550−
1551− newDetails.status = ERROR_RUNNING_SCRIPT;
1602+ newDetails.status = EntityScriptStatus::ERROR_RUNNING_SCRIPT;
15521603 newDetails.errorInfo = "Could not find constructor";
15531604 _entityScripts[entityID] = newDetails;
15541605 emit entityScriptDetailsUpdated();
15551606
1607+ qCDebug(scriptengine) << "----- ScriptEngine::entityScriptContentAvailable -- failed to run (" << scriptOrURL << ")";
15561608 return; // done processing script
15571609 }
15581610
@@ -1569,6 +1621,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 10 unmodified lines
15691621 };
15701622 doWithEnvironment(entityID, sandboxURL, initialization);
15711623
1624+ newDetails.status = EntityScriptStatus::RUNNING;
15721625 newDetails.scriptObject = entityScriptObject;
15731626 newDetails.lastModified = lastModified;
15741627 newDetails.definingSandboxURL = sandboxURL;
@@ -1600,10 +1653,12 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) { · 25 unmodified lines
16001653 #endif
16011654
16021655 if (_entityScripts.contains(entityID)) {
1603− if (_entityScripts[entityID].status == RUNNING) {
1656+ if (isEntityScriptRunning(entityID)) {
16041657 callEntityScriptMethod(entityID, "unload");
16051658 }
1606− _entityScripts.remove(entityID);
1659+ EntityScriptDetails details;
1660+ details.status = EntityScriptStatus::UNLOADED;
1661+ _entityScripts[entityID] = details;
16071662 stopAllTimersForEntityScript(entityID);
16081663 emit entityScriptDetailsUpdated();
16091664 }
@@ -1622,9 +1677,7 @@ void ScriptEngine::unloadAllEntityScripts() { · 12 unmodified lines
16221677 qCDebug(scriptengine) << "ScriptEngine::unloadAllEntityScripts() called on correct thread [" << thread() << "]";
16231678 #endif
16241679 foreach(const EntityItemID& entityID, _entityScripts.keys()) {
1625− if (_entityScripts[entityID].status == RUNNING) {
1626− callEntityScriptMethod(entityID, "unload");
1627− }
1680+ unloadEntityScript(entityID);
16281681 }
16291682 _entityScripts.clear();
16301683 emit entityScriptDetailsUpdated();
@@ -1641,7 +1694,7 @@ void ScriptEngine::unloadAllEntityScripts() { · 10 unmodified lines
16411694 }
16421695
16431696 void ScriptEngine::refreshFileScript(const EntityItemID& entityID) {
1644− if (!_entityScripts.contains(entityID)) {
1697+ if (!HIFI_AUTOREFRESH_FILE_SCRIPTS || !_entityScripts.contains(entityID)) {
16451698 return;
16461699 }
16471700
@@ -1663,8 +1716,8 @@ void ScriptEngine::refreshFileScript(const EntityItemID& entityID) { · 15 unmodified lines
16631716 file.open(QIODevice::ReadOnly);
16641717 QString scriptContents = QTextStream(&file).readAll();
16651718 this->unloadEntityScript(entityID);
1666− this->entityScriptContentAvailable(entityID, details.scriptText, scriptContents, true, true);
1667− if (!_entityScripts.contains(entityID) || _entityScripts[entityID].status != RUNNING) {
1719+ this->entityScriptContentAvailable(entityID, details.scriptText, scriptContents, true, true, "Success");
1720+ if (!isEntityScriptRunning(entityID)) {
16681721 scriptWarningMessage("Reload script " + details.scriptText + " failed");
16691722 } else {
16701723 details = _entityScripts[entityID];
@@ -1692,7 +1745,10 @@ void ScriptEngine::doWithEnvironment(const EntityItemID& entityID, const QUrl& s · 21 unmodified lines
16921745 #else
16931746 operation();
16941747 #endif
1695− hadUncaughtExceptions(*this, _fileNameString, this);
1748+ if (hasUncaughtException()) {
1749+ reportUncaughtException();
1750+ clearExceptions();
1751+ }
16961752
16971753 currentEntityIdentifier = oldIdentifier;
16981754 currentSandboxURL = oldSandboxURL;
@@ -1722,8 +1778,10 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 23 unmodified lines
17221778 "entityID:" << entityID << "methodName:" << methodName;
17231779 #endif
17241780
1725− refreshFileScript(entityID);
1726− if (_entityScripts.contains(entityID) && _entityScripts[entityID].status == RUNNING) {
1781+ if (HIFI_AUTOREFRESH_FILE_SCRIPTS && methodName != "unload") {
1782+ refreshFileScript(entityID);
1783+ }
1784+ if (isEntityScriptRunning(entityID)) {
17271785 EntityScriptDetails details = _entityScripts[entityID];
17281786 QScriptValue entityScript = details.scriptObject; // previously loaded
17291787 if (entityScript.property(methodName).isFunction()) {
@@ -1754,8 +1812,10 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 24 unmodified lines
17541812 "entityID:" << entityID << "methodName:" << methodName << "event: pointerEvent";
17551813 #endif
17561814
1757− refreshFileScript(entityID);
1758− if (_entityScripts.contains(entityID) && _entityScripts[entityID].status == RUNNING) {
1815+ if (HIFI_AUTOREFRESH_FILE_SCRIPTS) {
1816+ refreshFileScript(entityID);
1817+ }
1818+ if (isEntityScriptRunning(entityID)) {
17591819 EntityScriptDetails details = _entityScripts[entityID];
17601820 QScriptValue entityScript = details.scriptObject; // previously loaded
17611821 if (entityScript.property(methodName).isFunction()) {
@@ -1787,8 +1847,10 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 25 unmodified lines
17871847 "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";
17881848 #endif
17891849
1790− refreshFileScript(entityID);
1791− if (_entityScripts.contains(entityID) && _entityScripts[entityID].status == RUNNING) {
1850+ if (HIFI_AUTOREFRESH_FILE_SCRIPTS) {
1851+ refreshFileScript(entityID);
1852+ }
1853+ if (isEntityScriptRunning(entityID)) {
17921854 EntityScriptDetails details = _entityScripts[entityID];
17931855 QScriptValue entityScript = details.scriptObject; // previously loaded
17941856 if (entityScript.property(methodName).isFunction()) {