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
68 68
69#include "MIDIEvent.h"69#include "MIDIEvent.h"
70 70
71static const QString SCRIPT_EXCEPTION_FORMAT = "[UncaughtException] %1 in %2:%3";71const QString BaseScriptEngine::SCRIPT_EXCEPTION_FORMAT { "[UncaughtException] %1 in %2:%3" };
72static const QScriptEngine::QObjectWrapOptions DEFAULT_QOBJECT_WRAP_OPTIONS =72static const QScriptEngine::QObjectWrapOptions DEFAULT_QOBJECT_WRAP_OPTIONS =
73 QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects;73 QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects;
74 74
75static const bool HIFI_AUTOREFRESH_FILE_SCRIPTS { true };
76
75Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature)77Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature)
76int functionSignatureMetaID = qRegisterMetaType<QScriptEngine::FunctionSignature>();78int functionSignatureMetaID = qRegisterMetaType<QScriptEngine::FunctionSignature>();
77 79
@@ -137,38 +139,48 @@ QString encodeEntityIdIntoEntityUrl(const QString& url, const QString& entityID) · 59 unmodified lines
137 return url + " [EntityID:" + entityID + "]";139 return url + " [EntityID:" + entityID + "]";
138}140}
139 141
140static bool hasCorrectSyntax(const QScriptProgram& program, ScriptEngine* reportingEngine) {142QString BaseScriptEngine::lintScript(const QString& sourceCode, const QString& fileName, const int lineNumber) {
141 const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode());143 const auto syntaxCheck = checkSyntax(sourceCode);
142 if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {144 if (syntaxCheck.state() != syntaxCheck.Valid) {
143 const auto error = syntaxCheck.errorMessage();145 const auto error = syntaxCheck.errorMessage();
144 const auto line = QString::number(syntaxCheck.errorLineNumber());146 const auto line = QString::number(syntaxCheck.errorLineNumber());
145 const auto column = QString::number(syntaxCheck.errorColumnNumber());147 const auto column = QString::number(syntaxCheck.errorColumnNumber());
146 const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, program.fileName(), line, column);148 const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, fileName, line, column);
147 reportingEngine->scriptErrorMessage(qPrintable(message));149 return message;
148 return false;150 }
149 }151 return QString();
150 return true;152}
151}153
152 154QString BaseScriptEngine::formatUncaughtException(const QString& overrideFileName) {
153static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName, ScriptEngine* reportingEngine, QString* exceptionMessage = nullptr) {155 QString message;
154 if (engine.hasUncaughtException()) {156 if (hasUncaughtException()) {
155 const auto backtrace = engine.uncaughtExceptionBacktrace();157 const auto error = uncaughtException();
156 const auto exception = engine.uncaughtException().toString();158 const auto backtrace = uncaughtExceptionBacktrace();
157 const auto line = QString::number(engine.uncaughtExceptionLineNumber());159 const auto exception = error.toString();
158 engine.clearExceptions();160 auto filename = overrideFileName;
161 if (filename.isEmpty()) {
162 QScriptContextInfo ctx { currentContext() };
163 filename = ctx.fileName();
164 }
165 const auto line = QString::number(uncaughtExceptionLineNumber());
159 166
160 QString message = QString(SCRIPT_EXCEPTION_FORMAT).arg(exception, fileName, line);167 message = QString(SCRIPT_EXCEPTION_FORMAT).arg(exception, overrideFileName, line);
161 if (!backtrace.empty()) {168 if (!backtrace.empty()) {
162 static const auto lineSeparator = "\n ";169 static const auto lineSeparator = "\n ";
163 message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator));170 message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator));
164 }171 }
165 reportingEngine->scriptErrorMessage(qPrintable(message));
166 if (exceptionMessage) {
167 *exceptionMessage = message;
168 }
169 return true;
170 }172 }
171 return false;173 return message;
174}
175
176QString 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;
172}184}
173 185
174ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const QString& fileNameString) :186ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const QString& fileNameString) :
@@ -181,7 +193,8 @@ ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const · 6 unmodified lines
181 DependencyManager::get<ScriptEngines>()->addScriptEngine(this);193 DependencyManager::get<ScriptEngines>()->addScriptEngine(this);
182 194
183 connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) {195 connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) {
184 hadUncaughtExceptions(*this, _fileNameString, this);196 reportUncaughtException();
197 clearExceptions();
185 });198 });
186 199
187 setProcessEventsInterval(MSECS_PER_SECOND);200 setProcessEventsInterval(MSECS_PER_SECOND);
@@ -301,7 +314,10 @@ void ScriptEngine::runDebuggable() { · 113 unmodified lines
301 }314 }
302 _lastUpdate = now;315 _lastUpdate = now;
303 // Debug and clear exceptions316 // Debug and clear exceptions
304 hadUncaughtExceptions(*this, _fileNameString, this);317 if (hasUncaughtException()) {
318 reportUncaughtException();
319 clearExceptions();
320 }
305 });321 });
306 322
307 timer->start(10);323 timer->start(10);
@@ -399,8 +415,6 @@ QString ScriptEngine::getFilename() const { · 91 unmodified lines
399 return lastPart;415 return lastPart;
400}416}
401 417
402
403// FIXME - switch this to the new model of ScriptCache callbacks
404void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) {418void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) {
405 if (_isRunning) {419 if (_isRunning) {
406 return;420 return;
@@ -410,19 +424,27 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) { · 3 unmodified lines
410 _fileNameString = url.toString();424 _fileNameString = url.toString();
411 _isReloading = reload;425 _isReloading = reload;
412 426
413 bool isPending;427 const auto maxRetries = 0; // for consistency with previous scriptCache->getScript() behavior
414 auto scriptCache = DependencyManager::get<ScriptCache>();428 auto scriptCache = DependencyManager::get<ScriptCache>();
415 scriptCache->getScript(url, this, isPending, reload);429 scriptCache->getScriptContents(url.toString(), [this](const QString& url, const QString& scriptContents, bool isURL, bool success, const QString&status) {
416}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 }
417 436
418// FIXME - switch this to the new model of ScriptCache callbacks437 _scriptContents = scriptContents;
419void ScriptEngine::scriptContentsAvailable(const QUrl& url, const QString& scriptContents) {438
420 _scriptContents = scriptContents;439 {
421 static const QString DEBUG_FLAG("#debug");440 static const QString DEBUG_FLAG("#debug");
422 if (QRegularExpression(DEBUG_FLAG).match(scriptContents).hasMatch()) {441 if (QRegularExpression(DEBUG_FLAG).match(scriptContents).hasMatch()) {
423 _debuggable = true;442 qCWarning(scriptengine) << "NOTE: ScriptEngine for " << QUrl(url).fileName() << " will be launched in debug mode";
424 }443 _debuggable = true;
425 emit scriptLoaded(url.toString());444 }
445 }
446 emit scriptLoaded(url);
447 }, reload, maxRetries);
426}448}
427 449
428void ScriptEngine::scriptErrorMessage(const QString& message) {450void ScriptEngine::scriptErrorMessage(const QString& message) {
@@ -440,12 +462,6 @@ void ScriptEngine::scriptInfoMessage(const QString& message) { · 11 unmodified lines
440 emit infoMessage(message);462 emit infoMessage(message);
441}463}
442 464
443// FIXME - switch this to the new model of ScriptCache callbacks
444void ScriptEngine::errorInLoadingScript(const QUrl& url) {
445 scriptErrorMessage("ERROR Loading file:" + url.toString());
446 emit errorLoadingScript(_fileNameString);
447}
448
449// Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of465// Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of
450// callAnimationStateHandler requires that the type be registered.466// callAnimationStateHandler requires that the type be registered.
451// These two are meaningful, if we ever do want to use them...467// These two are meaningful, if we ever do want to use them...
@@ -520,6 +536,15 @@ void ScriptEngine::init() { · 68 unmodified lines
520 536
521 auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();537 auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
522 entityScriptingInterface->init();538 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
523 548
524 // register various meta-types549 // register various meta-types
525 registerMetaTypes(this);550 registerMetaTypes(this);
@@ -850,17 +875,25 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi · 324 unmodified lines
850 }875 }
851 876
852 // Check syntax877 // Check syntax
853 const QScriptProgram program(sourceCode, fileName, lineNumber);878 auto syntaxError = lintScript(sourceCode, fileName);
854 if (!hasCorrectSyntax(program, this)) {879 QScriptProgram program { sourceCode, fileName, lineNumber };
880 if (!syntaxError.isEmpty() || program.isNull()) {
881 scriptErrorMessage(qPrintable(syntaxError));
855 return QScriptValue();882 return QScriptValue();
856 }883 }
857 884
858 ++_evaluatesPending;885 ++_evaluatesPending;
859 const auto result = QScriptEngine::evaluate(program);886 auto result = BaseScriptEngine::evaluate(program);
860 --_evaluatesPending;887 --_evaluatesPending;
861 888
862 const auto hadUncaughtException = hadUncaughtExceptions(*this, program.fileName(), this);889 if (hasUncaughtException()) {
863 emit evaluationFinished(result, hadUncaughtException);890 result = uncaughtException();
891 reportUncaughtException(program.fileName());
892 emit evaluationFinished(result, true);
893 clearExceptions();
894 } else {
895 emit evaluationFinished(result, false);
896 }
864 return result;897 return result;
865}898}
866 899
@@ -1009,7 +1042,10 @@ void ScriptEngine::run() { · 142 unmodified lines
1009 _lastUpdate = now;1042 _lastUpdate = now;
1010 1043
1011 // Debug and clear exceptions1044 // Debug and clear exceptions
1012 hadUncaughtExceptions(*this, _fileNameString, this);1045 if (hasUncaughtException()) {
1046 reportUncaughtException();
1047 clearExceptions();
1048 }
1013 }1049 }
1014 1050
1015 scriptInfoMessage("Script Engine stopping:" + getFilename());1051 scriptInfoMessage("Script Engine stopping:" + getFilename());
@@ -1299,12 +1335,12 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac · 283 unmodified lines
1299 EntityItemID capturedEntityIdentifier = currentEntityIdentifier;1335 EntityItemID capturedEntityIdentifier = currentEntityIdentifier;
1300 QUrl capturedSandboxURL = currentSandboxURL;1336 QUrl capturedSandboxURL = currentSandboxURL;
1301 1337
1302 auto evaluateScripts = [=](const QMap<QUrl, QString>& data) {1338 auto evaluateScripts = [=](const QMap<QUrl, QString>& data, const QMap<QUrl, QString>& status) {
1303 auto parentURL = _parentURL;1339 auto parentURL = _parentURL;
1304 for (QUrl url : urls) {1340 for (QUrl url : urls) {
1305 QString contents = data[url];1341 QString contents = data[url];
1306 if (contents.isNull()) {1342 if (contents.isNull()) {
1307 scriptErrorMessage("Error loading file: " + url.toString());1343 scriptErrorMessage("Error loading file (" + status[url] +"): " + url.toString());
1308 } else {1344 } else {
1309 std::lock_guard<std::recursive_mutex> lock(_lock);1345 std::lock_guard<std::recursive_mutex> lock(_lock);
1310 if (!_includedURLs.contains(url)) {1346 if (!_includedURLs.contains(url)) {
@@ -1368,7 +1404,7 @@ void ScriptEngine::load(const QString& loadFile) { · 57 unmodified lines
1368 }1404 }
1369 if (!currentEntityIdentifier.isInvalidID()) {1405 if (!currentEntityIdentifier.isInvalidID()) {
1370 scriptWarningMessage("Script.load() from entity script is ignored... loadFile:" 1406 scriptWarningMessage("Script.load() from entity script is ignored... loadFile:"
1371 + loadFile + "parent script:" + getFilename());1407 + loadFile + "parent script:" + getFilename() + "entity: " + currentEntityIdentifier.toString());
1372 return; // bail early1408 return; // bail early
1373 }1409 }
1374 1410
@@ -1411,11 +1447,16 @@ void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QStrin · 36 unmodified lines
1411int ScriptEngine::getNumRunningEntityScripts() const {1447int ScriptEngine::getNumRunningEntityScripts() const {
1412 int sum = 0;1448 int sum = 0;
1413 for (auto& st : _entityScripts) {1449 for (auto& st : _entityScripts) {
1414 if (st.status == RUNNING) {1450 if (st.status == EntityScriptStatus::RUNNING) {
1415 ++sum;1451 ++sum;
1416 }1452 }
1417 }1453 }
1418 return sum;1454 return sum;
1455
1456QString ScriptEngine::getEntityScriptStatus(const EntityItemID& entityID) {
1457 if (_entityScripts.contains(entityID))
1458 return EntityScriptStatus_::valueToKey(_entityScripts[entityID].status).toLower();
1459 return QString();
1419}1460}
1420 1461
1421bool ScriptEngine::getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const {1462bool ScriptEngine::getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const {
@@ -1430,25 +1471,37 @@ bool ScriptEngine::getEntityScriptDetails(const EntityItemID& entityID, EntitySc · 8 unmodified lines
1430// since all of these operations can be asynch we will always do the actual work in the response handler1471// since all of these operations can be asynch we will always do the actual work in the response handler
1431// for the download1472// for the download
1432void ScriptEngine::loadEntityScript(QWeakPointer<ScriptEngine> theEngine, const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {1473void 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
1433 // NOTE: If the script content is not currently in the cache, the LAMBDA here will be called on the Main Thread1486 // NOTE: If the script content is not currently in the cache, the LAMBDA here will be called on the Main Thread
1434 // which means we're guaranteed that it's not the correct thread for the ScriptEngine. This means1487 // which means we're guaranteed that it's not the correct thread for the ScriptEngine. This means
1435 // when we get into entityScriptContentAvailable() we will likely invokeMethod() to get it over1488 // when we get into entityScriptContentAvailable() we will likely invokeMethod() to get it over
1436 // to the "Entities" ScriptEngine thread.1489 // 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) {
1438 QSharedPointer<ScriptEngine> strongEngine = theEngine.toStrongRef();1491 QSharedPointer<ScriptEngine> strongEngine = theEngine.toStrongRef();
1439 if (strongEngine) {1492 if (strongEngine) {
1440#ifdef THREAD_DEBUGGING1493#ifdef THREAD_DEBUGGING
1441 qCDebug(scriptengine) << "ScriptEngine::entityScriptContentAvailable() IN LAMBDA contentAvailable on thread ["1494 qCDebug(scriptengine) << "ScriptEngine::entityScriptContentAvailable() IN LAMBDA contentAvailable on thread ["
1442 << QThread::currentThread() << "] expected thread [" << strongEngine->thread() << "]";1495 << QThread::currentThread() << "] expected thread [" << strongEngine->thread() << "]";
1443#endif1496#endif
1444 strongEngine->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success);1497 strongEngine->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success, status);
1445 }1498 }
1446 }, forceRedownload);1499 }, forceRedownload);
1447}1500}
1448 1501
1449// since all of these operations can be asynch we will always do the actual work in the response handler1502// since all of these operations can be asynch we will always do the actual work in the response handler
1450// for the download1503// for the download
1451void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success) {1504void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success , const QString& status) {
1452 if (QThread::currentThread() != thread()) {1505 if (QThread::currentThread() != thread()) {
1453#ifdef THREAD_DEBUGGING1506#ifdef THREAD_DEBUGGING
1454 qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::entityScriptContentAvailable() called on wrong thread ["1507 qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::entityScriptContentAvailable() called on wrong thread ["
@@ -1462,7 +1515,8 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 7 unmodified lines
1462 Q_ARG(const QString&, scriptOrURL),1515 Q_ARG(const QString&, scriptOrURL),
1463 Q_ARG(const QString&, contents),1516 Q_ARG(const QString&, contents),
1464 Q_ARG(bool, isURL),1517 Q_ARG(bool, isURL),
1465 Q_ARG(bool, success));1518 Q_ARG(bool, success),
1519 Q_ARG(const QString&, status));
1466 return;1520 return;
1467 }1521 }
1468 1522
@@ -1478,22 +1532,21 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 9 unmodified lines
1478 newDetails.scriptText = scriptOrURL;1532 newDetails.scriptText = scriptOrURL;
1479 1533
1480 if (!success) {1534 if (!success) {
1481 newDetails.status = ERROR_LOADING_SCRIPT;1535 newDetails.status = EntityScriptStatus::ERROR_LOADING_SCRIPT;
1482 newDetails.errorInfo = "Failed to load script";1536 newDetails.errorInfo = "Failed to load script (" + status + ")";
1483 _entityScripts[entityID] = newDetails;1537 _entityScripts[entityID] = newDetails;
1484 emit entityScriptDetailsUpdated();1538 emit entityScriptDetailsUpdated();
1485 return;1539 return;
1486 }1540 }
1487 1541
1488 QScriptProgram program(contents, fileName);1542 auto syntaxError = lintScript(contents, fileName);
1489 if (!hasCorrectSyntax(program, this)) {1543 QScriptProgram program { contents, fileName };
1490 if (!isFileUrl) {1544 if (!syntaxError.isNull() || program.isNull()) {
1491 scriptCache->addScriptToBadScriptList(scriptOrURL);1545 newDetails.status = EntityScriptStatus::ERROR_RUNNING_SCRIPT;
1492 }1546 newDetails.errorInfo = QString("Bad syntax (%1)").arg(syntaxError);
1493 newDetails.status = ERROR_RUNNING_SCRIPT;
1494 newDetails.errorInfo = "Bad syntax";
1495 _entityScripts[entityID] = newDetails;1547 _entityScripts[entityID] = newDetails;
1496 emit entityScriptDetailsUpdated();1548 emit entityScriptDetailsUpdated();
1549 qCDebug(scriptengine) << newDetails.errorInfo << scriptOrURL;
1497 return; // done processing script1550 return; // done processing script
1498 }1551 }
1499 1552
@@ -1502,16 +1555,18 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 2 unmodified lines
1502 }1555 }
1503 1556
1504 const int SANDBOX_TIMEOUT = 0.25 * MSECS_PER_SECOND;1557 const int SANDBOX_TIMEOUT = 0.25 * MSECS_PER_SECOND;
1505 QScriptEngine sandbox;1558 BaseScriptEngine sandbox;
1506 sandbox.setProcessEventsInterval(SANDBOX_TIMEOUT);1559 sandbox.setProcessEventsInterval(SANDBOX_TIMEOUT);
1507 QScriptValue testConstructor;1560 QScriptValue testConstructor;
1508 {1561 {
1509 QTimer timeout;1562 QTimer timeout;
1510 timeout.setSingleShot(true);1563 timeout.setSingleShot(true);
1511 timeout.start(SANDBOX_TIMEOUT);1564 timeout.start(SANDBOX_TIMEOUT);
1512 connect(&timeout, &QTimer::timeout, [&sandbox, SANDBOX_TIMEOUT]{1565 connect(&timeout, &QTimer::timeout, [&sandbox, SANDBOX_TIMEOUT, scriptOrURL]{
1513 auto context = sandbox.currentContext();1566 auto context = sandbox.currentContext();
1514 if (context) {1567 if (context) {
1568 qCDebug(scriptengine) << "ScriptEngine::entityScriptContentAvailable timeout(" << scriptOrURL << ")";
1569
1515 // Guard against infinite loops and non-performant code1570 // Guard against infinite loops and non-performant code
1516 context->throwError(QString("Timed out (entity constructors are limited to %1ms)").arg(SANDBOX_TIMEOUT));1571 context->throwError(QString("Timed out (entity constructors are limited to %1ms)").arg(SANDBOX_TIMEOUT));
1517 }1572 }
@@ -1519,13 +1574,13 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 1 unmodified line
1519 testConstructor = sandbox.evaluate(program);1574 testConstructor = sandbox.evaluate(program);
1520 }1575 }
1521 1576
1522 QString exceptionMessage;1577 QString exceptionMessage = sandbox.formatUncaughtException(program.fileName());
1523 if (hadUncaughtExceptions(sandbox, program.fileName(), this, &exceptionMessage)) {1578 if (!exceptionMessage.isNull()) {
1524 newDetails.status = ERROR_RUNNING_SCRIPT;1579 newDetails.status = EntityScriptStatus::ERROR_RUNNING_SCRIPT;
1525 newDetails.errorInfo = exceptionMessage;1580 newDetails.errorInfo = exceptionMessage;
1526 _entityScripts[entityID] = newDetails;1581 _entityScripts[entityID] = newDetails;
1527 emit entityScriptDetailsUpdated();1582 emit entityScriptDetailsUpdated();
1528 1583 qCDebug(scriptengine) << "----- ScriptEngine::entityScriptContentAvailable -- hadUncaughtExceptions (" << scriptOrURL << ")";
1529 return;1584 return;
1530 }1585 }
1531 1586
@@ -1544,15 +1599,12 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 12 unmodified lines
1544 + "," + testConstructorValue1599 + "," + testConstructorValue
1545 + "," + scriptOrURL);1600 + "," + scriptOrURL);
1546 1601
1547 if (!isFileUrl) {1602 newDetails.status = EntityScriptStatus::ERROR_RUNNING_SCRIPT;
1548 scriptCache->addScriptToBadScriptList(scriptOrURL);
1549 }
1550
1551 newDetails.status = ERROR_RUNNING_SCRIPT;
1552 newDetails.errorInfo = "Could not find constructor";1603 newDetails.errorInfo = "Could not find constructor";
1553 _entityScripts[entityID] = newDetails;1604 _entityScripts[entityID] = newDetails;
1554 emit entityScriptDetailsUpdated();1605 emit entityScriptDetailsUpdated();
1555 1606
1607 qCDebug(scriptengine) << "----- ScriptEngine::entityScriptContentAvailable -- failed to run (" << scriptOrURL << ")";
1556 return; // done processing script1608 return; // done processing script
1557 }1609 }
1558 1610
@@ -1569,6 +1621,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 10 unmodified lines
1569 };1621 };
1570 doWithEnvironment(entityID, sandboxURL, initialization);1622 doWithEnvironment(entityID, sandboxURL, initialization);
1571 1623
1624 newDetails.status = EntityScriptStatus::RUNNING;
1572 newDetails.scriptObject = entityScriptObject;1625 newDetails.scriptObject = entityScriptObject;
1573 newDetails.lastModified = lastModified;1626 newDetails.lastModified = lastModified;
1574 newDetails.definingSandboxURL = sandboxURL;1627 newDetails.definingSandboxURL = sandboxURL;
@@ -1600,10 +1653,12 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) { · 25 unmodified lines
1600#endif1653#endif
1601 1654
1602 if (_entityScripts.contains(entityID)) {1655 if (_entityScripts.contains(entityID)) {
1603 if (_entityScripts[entityID].status == RUNNING) {1656 if (isEntityScriptRunning(entityID)) {
1604 callEntityScriptMethod(entityID, "unload");1657 callEntityScriptMethod(entityID, "unload");
1605 }1658 }
1606 _entityScripts.remove(entityID);1659 EntityScriptDetails details;
1660 details.status = EntityScriptStatus::UNLOADED;
1661 _entityScripts[entityID] = details;
1607 stopAllTimersForEntityScript(entityID);1662 stopAllTimersForEntityScript(entityID);
1608 emit entityScriptDetailsUpdated();1663 emit entityScriptDetailsUpdated();
1609 }1664 }
@@ -1622,9 +1677,7 @@ void ScriptEngine::unloadAllEntityScripts() { · 12 unmodified lines
1622 qCDebug(scriptengine) << "ScriptEngine::unloadAllEntityScripts() called on correct thread [" << thread() << "]";1677 qCDebug(scriptengine) << "ScriptEngine::unloadAllEntityScripts() called on correct thread [" << thread() << "]";
1623#endif1678#endif
1624 foreach(const EntityItemID& entityID, _entityScripts.keys()) {1679 foreach(const EntityItemID& entityID, _entityScripts.keys()) {
1625 if (_entityScripts[entityID].status == RUNNING) {1680 unloadEntityScript(entityID);
1626 callEntityScriptMethod(entityID, "unload");
1627 }
1628 }1681 }
1629 _entityScripts.clear();1682 _entityScripts.clear();
1630 emit entityScriptDetailsUpdated();1683 emit entityScriptDetailsUpdated();
@@ -1641,7 +1694,7 @@ void ScriptEngine::unloadAllEntityScripts() { · 10 unmodified lines
1641}1694}
1642 1695
1643void ScriptEngine::refreshFileScript(const EntityItemID& entityID) {1696void ScriptEngine::refreshFileScript(const EntityItemID& entityID) {
1644 if (!_entityScripts.contains(entityID)) {1697 if (!HIFI_AUTOREFRESH_FILE_SCRIPTS || !_entityScripts.contains(entityID)) {
1645 return;1698 return;
1646 }1699 }
1647 1700
@@ -1663,8 +1716,8 @@ void ScriptEngine::refreshFileScript(const EntityItemID& entityID) { · 15 unmodified lines
1663 file.open(QIODevice::ReadOnly);1716 file.open(QIODevice::ReadOnly);
1664 QString scriptContents = QTextStream(&file).readAll();1717 QString scriptContents = QTextStream(&file).readAll();
1665 this->unloadEntityScript(entityID);1718 this->unloadEntityScript(entityID);
1666 this->entityScriptContentAvailable(entityID, details.scriptText, scriptContents, true, true);1719 this->entityScriptContentAvailable(entityID, details.scriptText, scriptContents, true, true, "Success");
1667 if (!_entityScripts.contains(entityID) || _entityScripts[entityID].status != RUNNING) {1720 if (!isEntityScriptRunning(entityID)) {
1668 scriptWarningMessage("Reload script " + details.scriptText + " failed");1721 scriptWarningMessage("Reload script " + details.scriptText + " failed");
1669 } else {1722 } else {
1670 details = _entityScripts[entityID];1723 details = _entityScripts[entityID];
@@ -1692,7 +1745,10 @@ void ScriptEngine::doWithEnvironment(const EntityItemID& entityID, const QUrl& s · 21 unmodified lines
1692#else1745#else
1693 operation();1746 operation();
1694#endif1747#endif
1695 hadUncaughtExceptions(*this, _fileNameString, this);1748 if (hasUncaughtException()) {
1749 reportUncaughtException();
1750 clearExceptions();
1751 }
1696 1752
1697 currentEntityIdentifier = oldIdentifier;1753 currentEntityIdentifier = oldIdentifier;
1698 currentSandboxURL = oldSandboxURL;1754 currentSandboxURL = oldSandboxURL;
@@ -1722,8 +1778,10 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 23 unmodified lines
1722 "entityID:" << entityID << "methodName:" << methodName;1778 "entityID:" << entityID << "methodName:" << methodName;
1723#endif1779#endif
1724 1780
1725 refreshFileScript(entityID);1781 if (HIFI_AUTOREFRESH_FILE_SCRIPTS && methodName != "unload") {
1726 if (_entityScripts.contains(entityID) && _entityScripts[entityID].status == RUNNING) {1782 refreshFileScript(entityID);
1783 }
1784 if (isEntityScriptRunning(entityID)) {
1727 EntityScriptDetails details = _entityScripts[entityID];1785 EntityScriptDetails details = _entityScripts[entityID];
1728 QScriptValue entityScript = details.scriptObject; // previously loaded1786 QScriptValue entityScript = details.scriptObject; // previously loaded
1729 if (entityScript.property(methodName).isFunction()) {1787 if (entityScript.property(methodName).isFunction()) {
@@ -1754,8 +1812,10 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 24 unmodified lines
1754 "entityID:" << entityID << "methodName:" << methodName << "event: pointerEvent";1812 "entityID:" << entityID << "methodName:" << methodName << "event: pointerEvent";
1755#endif1813#endif
1756 1814
1757 refreshFileScript(entityID);1815 if (HIFI_AUTOREFRESH_FILE_SCRIPTS) {
1758 if (_entityScripts.contains(entityID) && _entityScripts[entityID].status == RUNNING) {1816 refreshFileScript(entityID);
1817 }
1818 if (isEntityScriptRunning(entityID)) {
1759 EntityScriptDetails details = _entityScripts[entityID];1819 EntityScriptDetails details = _entityScripts[entityID];
1760 QScriptValue entityScript = details.scriptObject; // previously loaded1820 QScriptValue entityScript = details.scriptObject; // previously loaded
1761 if (entityScript.property(methodName).isFunction()) {1821 if (entityScript.property(methodName).isFunction()) {
@@ -1787,8 +1847,10 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 25 unmodified lines
1787 "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";1847 "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";
1788#endif1848#endif
1789 1849
1790 refreshFileScript(entityID);1850 if (HIFI_AUTOREFRESH_FILE_SCRIPTS) {
1791 if (_entityScripts.contains(entityID) && _entityScripts[entityID].status == RUNNING) {1851 refreshFileScript(entityID);
1852 }
1853 if (isEntityScriptRunning(entityID)) {
1792 EntityScriptDetails details = _entityScripts[entityID];1854 EntityScriptDetails details = _entityScripts[entityID];
1793 QScriptValue entityScript = details.scriptObject; // previously loaded1855 QScriptValue entityScript = details.scriptObject; // previously loaded
1794 if (entityScript.property(methodName).isFunction()) {1856 if (entityScript.property(methodName).isFunction()) {