History

Merge branch 'master' into midi

burtsloane committed 9 years ago

Commit d4ae2982 parents a960ea5 4a3e3cf783 files · +30471 −12950Browse files

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

Unified · Split

libraries/script-engine/src/ScriptEngine.cpp+71−49
@@ -24,6 +24,8 @@ · 23 unmodified lines
2424
2525 #include <QtWidgets/QMainWindow>
2626 #include <QtWidgets/QApplication>
27+#include <QtWidgets/QMenuBar>
28+#include <QtWidgets/QMenu>
2729
2830 #include <QtNetwork/QNetworkRequest>
2931 #include <QtNetwork/QNetworkReply>
@@ -34,6 +36,7 @@ · 4 unmodified lines
3436
3537 #include <QtScriptTools/QScriptEngineDebugger>
3638
39+#include <shared/QtHelpers.h>
3740 #include <AudioConstants.h>
3841 #include <AudioEffectOptions.h>
3942 #include <AvatarData.h>
@@ -48,7 +51,6 @@ · 8 unmodified lines
4851 #include <ScriptAvatarData.h>
4952 #include <udt/PacketHeaders.h>
5053 #include <UUID.h>
51−#include <ui/Menu.h>
5254
5355 #include <controllers/ScriptingInterface.h>
5456 #include <AnimationObject.h>
@@ -70,7 +72,6 @@ · 15 unmodified lines
7072 #include "WebSocketClass.h"
7173 #include "RecordingScriptingInterface.h"
7274 #include "ScriptEngines.h"
73−#include "TabletScriptingInterface.h"
7475 #include "ModelScriptingInterface.h"
7576
7677
@@ -173,7 +174,7 @@ ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const · 96 unmodified lines
173174 emit unhandledException(exception);
174175 }
175176 }, Qt::DirectConnection);
176−
177+
177178 setProcessEventsInterval(MSECS_PER_SECOND);
178179 if (isEntityServerScript()) {
179180 qCDebug(scriptengine) << "isEntityServerScript() -- limiting maxRetries to 1";
@@ -282,7 +283,7 @@ void ScriptEngine::runDebuggable() { · 102 unmodified lines
282283 scriptDebugMenu = nullptr;
283284 }
284285 }
285− disconnect(timer);
286+ disconnect(timer);
286287 });
287288
288289 connect(timer, &QTimer::timeout, [this, timer] {
@@ -341,7 +342,7 @@ void ScriptEngine::runInThread() { · 52 unmodified lines
341342 QThread* workerThread = new QThread();
342343 workerThread->setObjectName(QString("js:") + getFilename().replace("about:",""));
343344 moveToThread(workerThread);
344−
345+
345346 // NOTE: If you connect any essential signals for proper shutdown or cleanup of
346347 // the script engine, make sure to add code to "reconnect" them to the
347348 // disconnectNonEssentialSignals() method
@@ -437,12 +438,12 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) { · 89 unmodified lines
437438 _fileNameString = url.toString();
438439 _isReloading = reload;
439440
440− // Check that script has a supported file extension
441+ // Check that script has a supported file extension
441442 if (!hasValidScriptSuffix(_fileNameString)) {
442443 scriptErrorMessage("File extension of file: " + _fileNameString + " is not a currently supported script type");
443444 emit errorLoadingScript(_fileNameString);
444445 return;
445− }
446+ }
446447
447448 const auto maxRetries = 0; // for consistency with previous scriptCache->getScript() behavior
448449 auto scriptCache = DependencyManager::get<ScriptCache>();
@@ -473,20 +474,24 @@ void ScriptEngine::scriptErrorMessage(const QString& message) { · 24 unmodified lines
473474 }
474475
475476 void ScriptEngine::scriptWarningMessage(const QString& message) {
476− qCWarning(scriptengine) << message;
477+ qCWarning(scriptengine) << qPrintable(message);
477478 emit warningMessage(message, getFilename());
478479 }
479480
480481 void ScriptEngine::scriptInfoMessage(const QString& message) {
481− qCInfo(scriptengine) << message;
482+ qCInfo(scriptengine) << qPrintable(message);
482483 emit infoMessage(message, getFilename());
483484 }
484485
485486 void ScriptEngine::scriptPrintedMessage(const QString& message) {
486− qCDebug(scriptengine) << message;
487+ qCDebug(scriptengine) << qPrintable(message);
487488 emit printedMessage(message, getFilename());
488489 }
489490
491+void ScriptEngine::clearDebugLogWindow() {
492+ emit clearDebugWindow();
493+}
494+
490495 // Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of
491496 // callAnimationStateHandler requires that the type be registered.
492497 // These two are meaningful, if we ever do want to use them...
@@ -671,8 +676,18 @@ void ScriptEngine::init() { · 178 unmodified lines
671676 registerGlobalObject("Mat4", &_mat4Library);
672677 registerGlobalObject("Uuid", &_uuidLibrary);
673678 registerGlobalObject("Messages", DependencyManager::get<MessagesClient>().data());
674−
675679 registerGlobalObject("File", new FileScriptingInterface(this));
680+ registerGlobalObject("console", &_consoleScriptingInterface);
681+ registerFunction("console", "info", ConsoleScriptingInterface::info, currentContext()->argumentCount());
682+ registerFunction("console", "log", ConsoleScriptingInterface::log, currentContext()->argumentCount());
683+ registerFunction("console", "debug", ConsoleScriptingInterface::debug, currentContext()->argumentCount());
684+ registerFunction("console", "warn", ConsoleScriptingInterface::warn, currentContext()->argumentCount());
685+ registerFunction("console", "error", ConsoleScriptingInterface::error, currentContext()->argumentCount());
686+ registerFunction("console", "exception", ConsoleScriptingInterface::exception, currentContext()->argumentCount());
687+ registerFunction("console", "assert", ConsoleScriptingInterface::assertion, currentContext()->argumentCount());
688+ registerFunction("console", "group", ConsoleScriptingInterface::group, 1);
689+ registerFunction("console", "groupCollapsed", ConsoleScriptingInterface::groupCollapsed, 1);
690+ registerFunction("console", "groupEnd", ConsoleScriptingInterface::groupEnd, 0);
676691
677692 qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue);
678693 qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue);
@@ -686,8 +701,6 @@ void ScriptEngine::init() { · 7 unmodified lines
686701 // constants
687702 globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));
688703
689− registerGlobalObject("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
690− qScriptRegisterMetaType(this, tabletToScriptValue, tabletFromScriptValue);
691704 registerGlobalObject("Assets", &_assetScriptingInterface);
692705 registerGlobalObject("Resources", DependencyManager::get<ResourceScriptingInterface>().data());
693706
@@ -955,7 +968,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi · 261 unmodified lines
955968 qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
956969 "sourceCode:" << sourceCode << " fileName:" << fileName << "lineNumber:" << lineNumber;
957970 #endif
958− QMetaObject::invokeMethod(this, "evaluate", Qt::BlockingQueuedConnection,
971+ BLOCKING_INVOKE_METHOD(this, "evaluate",
959972 Q_RETURN_ARG(QScriptValue, result),
960973 Q_ARG(const QString&, sourceCode),
961974 Q_ARG(const QString&, fileName),
@@ -1009,6 +1022,7 @@ void ScriptEngine::run() { · 47 unmodified lines
10091022 emit runningStateChanged();
10101023
10111024 {
1025+ PROFILE_RANGE(script, _fileNameString);
10121026 evaluate(_scriptContents, _fileNameString);
10131027 maybeEmitUncaughtException(__FUNCTION__);
10141028 }
@@ -1035,46 +1049,43 @@ void ScriptEngine::run() { · 20 unmodified lines
10351049 auto beforeSleep = clock::now();
10361050
10371051 // Throttle to SCRIPT_FPS
1038− // We'd like to try to keep the script at a solid SCRIPT_FPS update rate. And so we will
1052+ // We'd like to try to keep the script at a solid SCRIPT_FPS update rate. And so we will
10391053 // calculate a sleepUntil to be the time from our start time until the original target
1040− // sleepUntil for this frame. This approach will allow us to "catch up" in the event
1041− // that some of our script udpates/frames take a little bit longer than the target average
1054+ // sleepUntil for this frame. This approach will allow us to "catch up" in the event
1055+ // that some of our script udpates/frames take a little bit longer than the target average
10421056 // to execute.
10431057 // NOTE: if we go to variable SCRIPT_FPS, then we will need to reconsider this approach
10441058 const std::chrono::microseconds TARGET_SCRIPT_FRAME_DURATION(USECS_PER_SECOND / SCRIPT_FPS + 1);
10451059 clock::time_point targetSleepUntil(startTime + (thisFrame++ * TARGET_SCRIPT_FRAME_DURATION));
10461060
1047− // However, if our sleepUntil is not at least our average update and timer execution time
1048− // into the future it means our script is taking too long in its updates, and we want to
1049− // punish the script a little bit. So we will force the sleepUntil to be at least our
1061+ // However, if our sleepUntil is not at least our average update and timer execution time
1062+ // into the future it means our script is taking too long in its updates, and we want to
1063+ // punish the script a little bit. So we will force the sleepUntil to be at least our
10501064 // averageUpdate + averageTimerPerFrame time into the future.
10511065 auto averageUpdate = totalUpdates / thisFrame;
10521066 auto averageTimerPerFrame = _totalTimerExecution / thisFrame;
10531067 auto averageTimerAndUpdate = averageUpdate + averageTimerPerFrame;
10541068 auto sleepUntil = std::max(targetSleepUntil, beforeSleep + averageTimerAndUpdate);
10551069
1056− // We don't want to actually sleep for too long, because it causes our scripts to hang
1057− // on shutdown and stop... so we want to loop and sleep until we've spent our time in
1070+ // We don't want to actually sleep for too long, because it causes our scripts to hang
1071+ // on shutdown and stop... so we want to loop and sleep until we've spent our time in
10581072 // purgatory, constantly checking to see if our script was asked to end
10591073 bool processedEvents = false;
1060− while (!_isFinished && clock::now() < sleepUntil) {
1061−
1062− {
1063− PROFILE_RANGE(script, "processEvents-sleep");
1064− QCoreApplication::processEvents(); // before we sleep again, give events a chance to process
1074+ if (!_isFinished) {
1075+ PROFILE_RANGE(script, "processEvents-sleep");
1076+ std::chrono::milliseconds sleepFor =
1077+ std::chrono::duration_cast<std::chrono::milliseconds>(sleepUntil - clock::now());
1078+ if (sleepFor > std::chrono::milliseconds(0)) {
1079+ QEventLoop loop;
1080+ QTimer timer;
1081+ timer.setSingleShot(true);
1082+ connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
1083+ timer.start(sleepFor.count());
1084+ loop.exec();
1085+ } else {
1086+ QCoreApplication::processEvents();
10651087 }
10661088 processedEvents = true;
1067−
1068− // If after processing events, we're past due, exit asap
1069− if (clock::now() >= sleepUntil) {
1070− break;
1071− }
1072−
1073− // We only want to sleep a small amount so that any pending events (like timers or invokeMethod events)
1074− // will be able to process quickly.
1075− static const int SMALL_SLEEP_AMOUNT = 100;
1076− auto smallSleepUntil = clock::now() + static_cast<std::chrono::microseconds>(SMALL_SLEEP_AMOUNT);
1077− std::this_thread::sleep_until(smallSleepUntil);
10781089 }
10791090
10801091 PROFILE_RANGE(script, "ScriptMainLoop");
@@ -1272,6 +1283,7 @@ void ScriptEngine::timerFired() { · 191 unmodified lines
12721283
12731284 // call the associated JS function, if it exists
12741285 if (timerData.function.isValid()) {
1286+ PROFILE_RANGE(script, __FUNCTION__);
12751287 auto preTimer = p_high_resolution_clock::now();
12761288 callWithEnvironment(timerData.definingEntityIdentifier, timerData.definingSandboxURL, timerData.function, timerData.function, QScriptValueList());
12771289 auto postTimer = p_high_resolution_clock::now();
@@ -1298,6 +1310,7 @@ QObject* ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int · 20 unmodified lines
12981310 // make sure the timer stops when the script does
12991311 connect(this, &ScriptEngine::scriptEnding, newTimer, &QTimer::stop);
13001312
1313+
13011314 CallbackData timerData = { function, currentEntityIdentifier, currentSandboxURL };
13021315 _timerFunctionMap.insert(newTimer, timerData);
13031316
@@ -1381,12 +1394,21 @@ void ScriptEngine::print(const QString& message) { · 77 unmodified lines
13811394 emit printedMessage(message, getFilename());
13821395 }
13831396
1397+
1398+void ScriptEngine::beginProfileRange(const QString& label) const {
1399+ PROFILE_SYNC_BEGIN(script, label.toStdString().c_str(), label.toStdString().c_str());
1400+}
1401+
1402+void ScriptEngine::endProfileRange(const QString& label) const {
1403+ PROFILE_SYNC_END(script, label.toStdString().c_str(), label.toStdString().c_str());
1404+}
1405+
13841406 // Script.require.resolve -- like resolvePath, but performs more validation and throws exceptions on invalid module identifiers (for consistency with Node.js)
13851407 QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& relativeTo) {
13861408 if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
13871409 return QString();
13881410 }
1389− QUrl defaultScriptsLoc = defaultScriptsLocation();
1411+ QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
13901412 QUrl url(moduleId);
13911413
13921414 auto displayId = moduleId;
@@ -1452,7 +1474,7 @@ QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& re · 59 unmodified lines
14521474 canonical.setPath(file.canonicalFilePath());
14531475 }
14541476
1455− bool disallowOutsideFiles = !defaultScriptsLocation().isParentOf(canonical) && !currentSandboxURL.isLocalFile();
1477+ bool disallowOutsideFiles = !PathUtils::defaultScriptsLocation().isParentOf(canonical) && !currentSandboxURL.isLocalFile();
14561478 if (disallowOutsideFiles && !PathUtils::isDescendantOf(canonical, currentSandboxURL)) {
14571479 return throwResolveError(makeError(message.arg(
14581480 QString("path '%1' outside of origin script '%2' '%3'")
@@ -1737,19 +1759,19 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac · 278 unmodified lines
17371759 return;
17381760 }
17391761 if (DependencyManager::get<ScriptEngines>()->isStopped()) {
1740− scriptWarningMessage("Script.include() while shutting down is ignored... includeFiles:"
1762+ scriptWarningMessage("Script.include() while shutting down is ignored... includeFiles:"
17411763 + includeFiles.join(",") + "parent script:" + getFilename());
17421764 return; // bail early
17431765 }
17441766 QList<QUrl> urls;
17451767
17461768 for (QString includeFile : includeFiles) {
1747− QString file = ResourceManager::normalizeURL(includeFile);
1769+ QString file = DependencyManager::get<ResourceManager>()->normalizeURL(includeFile);
17481770 QUrl thisURL;
17491771 bool isStandardLibrary = false;
17501772 if (file.startsWith("/~/")) {
17511773 thisURL = expandScriptUrl(QUrl::fromLocalFile(expandScriptPath(file)));
1752− QUrl defaultScriptsLoc = defaultScriptsLocation();
1774+ QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
17531775 if (!defaultScriptsLoc.isParentOf(thisURL)) {
17541776 scriptWarningMessage("Script.include() -- skipping" + file + "-- outside of standard libraries");
17551777 continue;
@@ -1761,7 +1783,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac · 5 unmodified lines
17611783
17621784 bool disallowOutsideFiles = thisURL.isLocalFile() && !isStandardLibrary && !currentSandboxURL.isLocalFile();
17631785 if (disallowOutsideFiles && !PathUtils::isDescendantOf(thisURL, currentSandboxURL)) {
1764− scriptWarningMessage("Script.include() ignoring file path" + thisURL.toString()
1786+ scriptWarningMessage("Script.include() ignoring file path" + thisURL.toString()
17651787 + "outside of original entity script" + currentSandboxURL.toString());
17661788 } else {
17671789 // We could also check here for CORS, but we don't yet.
@@ -1802,7 +1824,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac · 34 unmodified lines
18021824 clearExceptions();
18031825 }
18041826 } else {
1805− scriptWarningMessage("Script.include() skipping evaluation of previously included url:" + url.toString());
1827+ scriptPrintedMessage("Script.include() skipping evaluation of previously included url:" + url.toString());
18061828 }
18071829 }
18081830 }
@@ -1831,7 +1853,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac · 22 unmodified lines
18311853
18321854 void ScriptEngine::include(const QString& includeFile, QScriptValue callback) {
18331855 if (DependencyManager::get<ScriptEngines>()->isStopped()) {
1834− scriptWarningMessage("Script.include() while shutting down is ignored... includeFile:"
1856+ scriptWarningMessage("Script.include() while shutting down is ignored... includeFile:"
18351857 + includeFile + "parent script:" + getFilename());
18361858 return; // bail early
18371859 }
@@ -1849,12 +1871,12 @@ void ScriptEngine::load(const QString& loadFile) { · 11 unmodified lines
18491871 return;
18501872 }
18511873 if (DependencyManager::get<ScriptEngines>()->isStopped()) {
1852− scriptWarningMessage("Script.load() while shutting down is ignored... loadFile:"
1874+ scriptWarningMessage("Script.load() while shutting down is ignored... loadFile:"
18531875 + loadFile + "parent script:" + getFilename());
18541876 return; // bail early
18551877 }
18561878 if (!currentEntityIdentifier.isInvalidID()) {
1857− scriptWarningMessage("Script.load() from entity script is ignored... loadFile:"
1879+ scriptWarningMessage("Script.load() from entity script is ignored... loadFile:"
18581880 + loadFile + "parent script:" + getFilename() + "entity: " + currentEntityIdentifier.toString());
18591881 return; // bail early
18601882 }
@@ -2535,7 +2557,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 674 unmodified lines
25352557 qCDebug(scriptengine) << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] "
25362558 "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";
25372559 #endif
2538−
2560+
25392561 if (HIFI_AUTOREFRESH_FILE_SCRIPTS) {
25402562 refreshFileScript(entityID);
25412563 }