From 958d853b565237dc2c8aa649f62a1bfdf6cbd5aa Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 14:13:54 -0800 Subject: [PATCH 1/8] Add BatchLoader --- libraries/script-engine/src/BatchLoader.cpp | 82 +++++++++++++++++++++ libraries/script-engine/src/BatchLoader.h | 45 +++++++++++ 2 files changed, 127 insertions(+) create mode 100644 libraries/script-engine/src/BatchLoader.cpp create mode 100644 libraries/script-engine/src/BatchLoader.h diff --git a/libraries/script-engine/src/BatchLoader.cpp b/libraries/script-engine/src/BatchLoader.cpp new file mode 100644 index 0000000000..c44742c0b6 --- /dev/null +++ b/libraries/script-engine/src/BatchLoader.cpp @@ -0,0 +1,82 @@ +// +// BatchLoader.cpp +// libraries/script-engine/src +// +// Created by Ryan Huffman on 01/22/15 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include + +#include +#include "BatchLoader.h" +#include + +BatchLoader::BatchLoader(const QList& urls) + : QObject(), + _finished(false), + _urls(urls.toSet()), + _started(false), + _data() { +} + +void BatchLoader::start() { + if (_started) { + return; + } + + _started = true; + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + for (QUrl url : _urls) { + if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "ftp") { + QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url)); + + qDebug() << "Downloading file at" << url; + + connect(reply, &QNetworkReply::finished, [=]() { + if (reply->error()) { + emit errorLoadingFile(url); + _data.insert(url, QString()); + } else { + _data.insert(url, reply->readAll()); + } + reply->deleteLater(); + checkFinished(); + }); + + // If we end up being destroyed before the reply finishes, clean it up + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); + + } else { +#ifdef _WIN32 + QString fileName = url.toString(); +#else + QString fileName = url.toLocalFile(); +#endif + + qDebug() << "Reading file at " << fileName; + + QFile scriptFile(fileName); + if (scriptFile.open(QFile::ReadOnly | QFile::Text)) { + QTextStream in(&scriptFile); + _data.insert(url, in.readAll()); + // includeContents = in.readAll(); + } else { + emit errorLoadingFile(url); + _data.insert(url, QString()); + } + } + } + checkFinished(); +} + +void BatchLoader::checkFinished() { + if (!_finished && _urls.size() == _data.size()) { + _finished = true; + emit finished(_data); + } +} diff --git a/libraries/script-engine/src/BatchLoader.h b/libraries/script-engine/src/BatchLoader.h new file mode 100644 index 0000000000..f165aafed4 --- /dev/null +++ b/libraries/script-engine/src/BatchLoader.h @@ -0,0 +1,45 @@ +// +// BatchLoader.h +// libraries/script-engine/src +// +// Created by Ryan Huffman on 01/22/15 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_BatchLoader_h +#define hifi_BatchLoader_h + +#include +#include +#include +#include +#include +#include + +#include + +class BatchLoader : public QObject { + Q_OBJECT +public: + BatchLoader(const QList& urls) ; + + void start(); + bool isFinished() const { return _finished; }; + +signals: + void finished(const QMap& data); + void errorLoadingFile(QUrl url); + +private: + void checkFinished(); + + bool _finished; + bool _started; + QSet _urls; + QMap _data; +}; + +#endif // hifi_BatchLoader_h From 38b38eb063422d625afa3137ae094fb0a3984ebe Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 14:14:21 -0800 Subject: [PATCH 2/8] Update ScriptEngine to use BatchLoader --- libraries/script-engine/src/ScriptEngine.cpp | 77 +++++++++++--------- libraries/script-engine/src/ScriptEngine.h | 1 + 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0f860208f4..a505b63b69 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -31,6 +31,7 @@ #include "AnimationObject.h" #include "ArrayBufferViewClass.h" +#include "BatchLoader.h" #include "DataViewClass.h" #include "EventTypes.h" #include "MenuItemProperties.h" @@ -304,7 +305,7 @@ QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileN QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber); if (hasUncaughtException()) { int line = uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at (" << _fileNameString << ") line" << line << ": " << result.toString(); + qDebug() << "Uncaught exception at (" << _fileNameString << " : " << fileName << ") line" << line << ": " << result.toString(); } emit evaluationFinished(result, hasUncaughtException()); clearExceptions(); @@ -595,46 +596,52 @@ void ScriptEngine::print(const QString& message) { emit printedMessage(message); } -void ScriptEngine::include(const QString& includeFile) { - QUrl url = resolvePath(includeFile); - QString includeContents; +void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) { + QList urls; + for (QString file : includeFiles) { + urls.append(resolvePath(file)); + } - if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "ftp") { - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url)); - qDebug() << "Downloading included script at" << includeFile; - QEventLoop loop; - QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); - loop.exec(); - includeContents = reply->readAll(); - reply->deleteLater(); - } else { -#ifdef _WIN32 - QString fileName = url.toString(); -#else - QString fileName = url.toLocalFile(); -#endif - - QFile scriptFile(fileName); - if (scriptFile.open(QFile::ReadOnly | QFile::Text)) { - qDebug() << "Including file:" << fileName; - QTextStream in(&scriptFile); - includeContents = in.readAll(); - } else { - qDebug() << "ERROR Including file:" << fileName; - emit errorMessage("ERROR Including file:" + fileName); + BatchLoader* loader = new BatchLoader(urls); + + auto evaluateScripts = [=](const QMap& data) { + for (QUrl url : urls) { + QString contents = data[url]; + qDebug() << "About to load: " << url; + if (contents.isNull()) { + qDebug() << "Error loading file: " << url; + } else { + QScriptValue result = evaluate(contents, url.toString()); + } } - } - QScriptValue result = evaluate(includeContents); - if (hasUncaughtException()) { - int line = uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at (" << includeFile << ") line" << line << ":" << result.toString(); - emit errorMessage("Uncaught exception at (" + includeFile + ") line" + QString::number(line) + ":" + result.toString()); - clearExceptions(); + if (callback.isFunction()) { + QScriptValue(callback).call(); + } + + loader->deleteLater(); + }; + + connect(loader, &BatchLoader::finished, this, evaluateScripts); + + // If we are destroyed before the loader completes, make sure to clean it up + connect(this, &QObject::destroyed, loader, &QObject::deleteLater); + + loader->start(); + + if (!callback.isFunction() && !loader->isFinished()) { + QEventLoop loop; + QObject::connect(loader, &BatchLoader::finished, &loop, &QEventLoop::quit); + loop.exec(); } } +void ScriptEngine::include(const QString& includeFile) { + QStringList urls; + urls.append(includeFile); + include(urls); +} + void ScriptEngine::load(const QString& loadFile) { QUrl url = resolvePath(loadFile); emit loadScript(url.toString(), false); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 10f419937a..126e943982 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -96,6 +96,7 @@ public slots: QObject* setTimeout(const QScriptValue& function, int timeoutMS); void clearInterval(QObject* timer) { stopTimer(reinterpret_cast(timer)); } void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast(timer)); } + void include(const QStringList& includeFiles, QScriptValue callback = QScriptValue()); void include(const QString& includeFile); void load(const QString& loadfile); void print(const QString& message); From 4fd5f743651479c33db04c8e29ffb7d1a16bfc10 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 14:14:49 -0800 Subject: [PATCH 3/8] Update editEntities.js to load includes in parallel --- examples/editEntities.js | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/examples/editEntities.js b/examples/editEntities.js index 9187b624fd..f9d881f0bd 100644 --- a/examples/editEntities.js +++ b/examples/editEntities.js @@ -12,34 +12,37 @@ // HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; -Script.include("libraries/stringHelpers.js"); -Script.include("libraries/dataviewHelpers.js"); -Script.include("libraries/httpMultiPart.js"); -Script.include("libraries/modelUploader.js"); -Script.include("libraries/toolBars.js"); -Script.include("libraries/progressDialog.js"); -Script.include("libraries/entitySelectionTool.js"); +Script.include([ + "http://public.highfidelity.io/scripts/libraries/stringHelpers.js", + "http://public.highfidelity.io/scripts/libraries/dataviewHelpers.js", + "http://public.highfidelity.io/scripts/libraries/httpMultiPart.js", + "http://public.highfidelity.io/scripts/libraries/modelUploader.js", + "http://public.highfidelity.io/scripts/libraries/toolBars.js", + "http://public.highfidelity.io/scripts/libraries/progressDialog.js", + + "http://public.highfidelity.io/scripts/libraries/entitySelectionTool.js", + "http://public.highfidelity.io/scripts/libraries/ModelImporter.js", + + "http://public.highfidelity.io/scripts/libraries/ExportMenu.js", + "http://public.highfidelity.io/scripts/libraries/ToolTip.js", + + "http://public.highfidelity.io/scripts/libraries/entityPropertyDialogBox.js", + "http://public.highfidelity.io/scripts/libraries/entityCameraTool.js", + "http://public.highfidelity.io/scripts/libraries/gridTool.js", + "http://public.highfidelity.io/scripts/libraries/entityList.js", +]); + var selectionDisplay = SelectionDisplay; var selectionManager = SelectionManager; - -Script.include("libraries/ModelImporter.js"); var modelImporter = new ModelImporter(); - -Script.include("libraries/ExportMenu.js"); -Script.include("libraries/ToolTip.js"); - -Script.include("libraries/entityPropertyDialogBox.js"); var entityPropertyDialogBox = EntityPropertyDialogBox; -Script.include("libraries/entityCameraTool.js"); var cameraManager = new CameraManager(); -Script.include("libraries/gridTool.js"); var grid = Grid(); gridTool = GridTool({ horizontalGrid: grid }); -Script.include("libraries/entityList.js"); var entityListTool = EntityListTool(); var hasShownPropertiesTool = false; From b4132f1e7dd596b384d1e61c2677971a63bb3b7e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 14:16:36 -0800 Subject: [PATCH 4/8] Remove commented line --- libraries/script-engine/src/BatchLoader.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/script-engine/src/BatchLoader.cpp b/libraries/script-engine/src/BatchLoader.cpp index c44742c0b6..ed6591423f 100644 --- a/libraries/script-engine/src/BatchLoader.cpp +++ b/libraries/script-engine/src/BatchLoader.cpp @@ -64,7 +64,6 @@ void BatchLoader::start() { if (scriptFile.open(QFile::ReadOnly | QFile::Text)) { QTextStream in(&scriptFile); _data.insert(url, in.readAll()); - // includeContents = in.readAll(); } else { emit errorLoadingFile(url); _data.insert(url, QString()); From e27db48e262c6297d3956bb36dcfa2de7a1c9c24 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 14:20:42 -0800 Subject: [PATCH 5/8] Remove qDebug and unnecessary include --- libraries/script-engine/src/BatchLoader.h | 2 -- libraries/script-engine/src/ScriptEngine.cpp | 1 - 2 files changed, 3 deletions(-) diff --git a/libraries/script-engine/src/BatchLoader.h b/libraries/script-engine/src/BatchLoader.h index f165aafed4..6d19b2322f 100644 --- a/libraries/script-engine/src/BatchLoader.h +++ b/libraries/script-engine/src/BatchLoader.h @@ -19,8 +19,6 @@ #include #include -#include - class BatchLoader : public QObject { Q_OBJECT public: diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index a505b63b69..246f9d3be3 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -607,7 +607,6 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac auto evaluateScripts = [=](const QMap& data) { for (QUrl url : urls) { QString contents = data[url]; - qDebug() << "About to load: " << url; if (contents.isNull()) { qDebug() << "Error loading file: " << url; } else { From 22cdf1cb226928fd806ebb415e1b191f3dace368 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 14:24:20 -0800 Subject: [PATCH 6/8] Remove BatchLoader::errorLoadingFile and reorder properties --- libraries/script-engine/src/BatchLoader.cpp | 4 +--- libraries/script-engine/src/BatchLoader.h | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/libraries/script-engine/src/BatchLoader.cpp b/libraries/script-engine/src/BatchLoader.cpp index ed6591423f..e2c345ce16 100644 --- a/libraries/script-engine/src/BatchLoader.cpp +++ b/libraries/script-engine/src/BatchLoader.cpp @@ -18,9 +18,9 @@ BatchLoader::BatchLoader(const QList& urls) : QObject(), + _started(false), _finished(false), _urls(urls.toSet()), - _started(false), _data() { } @@ -39,7 +39,6 @@ void BatchLoader::start() { connect(reply, &QNetworkReply::finished, [=]() { if (reply->error()) { - emit errorLoadingFile(url); _data.insert(url, QString()); } else { _data.insert(url, reply->readAll()); @@ -65,7 +64,6 @@ void BatchLoader::start() { QTextStream in(&scriptFile); _data.insert(url, in.readAll()); } else { - emit errorLoadingFile(url); _data.insert(url, QString()); } } diff --git a/libraries/script-engine/src/BatchLoader.h b/libraries/script-engine/src/BatchLoader.h index 6d19b2322f..cda040d219 100644 --- a/libraries/script-engine/src/BatchLoader.h +++ b/libraries/script-engine/src/BatchLoader.h @@ -29,13 +29,12 @@ public: signals: void finished(const QMap& data); - void errorLoadingFile(QUrl url); private: void checkFinished(); - bool _finished; bool _started; + bool _finished; QSet _urls; QMap _data; }; From ce9e8910e48c464c7e89f407370a9db52f2361d3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 29 Jan 2015 09:04:57 -0800 Subject: [PATCH 7/8] Add callback to QScriptEngine::include(QString) --- libraries/script-engine/src/ScriptEngine.cpp | 4 ++-- libraries/script-engine/src/ScriptEngine.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 246f9d3be3..b400dd5cfb 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -635,10 +635,10 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac } } -void ScriptEngine::include(const QString& includeFile) { +void ScriptEngine::include(const QString& includeFile, QScriptValue callback) { QStringList urls; urls.append(includeFile); - include(urls); + include(urls, callback); } void ScriptEngine::load(const QString& loadFile) { diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 126e943982..f78a14bffa 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -97,7 +97,7 @@ public slots: void clearInterval(QObject* timer) { stopTimer(reinterpret_cast(timer)); } void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast(timer)); } void include(const QStringList& includeFiles, QScriptValue callback = QScriptValue()); - void include(const QString& includeFile); + void include(const QString& includeFile, QScriptValue callback = QScriptValue()); void load(const QString& loadfile); void print(const QString& message); QUrl resolvePath(const QString& path) const; From e6caefa023f1e04095c23c76ae593557a07d64fc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 29 Jan 2015 09:08:55 -0800 Subject: [PATCH 8/8] Add comment to ScriptEngine::include() --- libraries/script-engine/src/ScriptEngine.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index b400dd5cfb..a002950d46 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -112,7 +112,7 @@ void ScriptEngine::setIsAvatar(bool isAvatar) { _avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); _avatarBillboardTimer->start(AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS); } - + if (!_isAvatar) { delete _avatarIdentityTimer; _avatarIdentityTimer = NULL; @@ -596,6 +596,12 @@ void ScriptEngine::print(const QString& message) { emit printedMessage(message); } +/** + * If a callback is specified, the included files will be loaded asynchronously and the callback will be called + * when all of the files have finished loading. + * If no callback is specified, the included files will be loaded synchronously and will block execution until + * all of the files have finished loading. + */ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) { QList urls; for (QString file : includeFiles) {