rework ScriptEngine lifetimes using 'smart' pointers

This commit is contained in:
Seth Alves 2017-09-14 10:42:21 -07:00
parent 49ab8af428
commit d89f2b77da
18 changed files with 135 additions and 146 deletions

View file

@ -354,15 +354,16 @@ void Agent::scriptRequestFinished() {
void Agent::executeScript() { void Agent::executeScript() {
_scriptEngine = std::unique_ptr<ScriptEngine>(new ScriptEngine(ScriptEngine::AGENT_SCRIPT, _scriptContents, _payload)); _scriptEngine = scriptEngineFactory(ScriptEngine::AGENT_SCRIPT, _scriptContents, _payload);
_scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do _scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do
DependencyManager::get<RecordingScriptingInterface>()->setScriptEngine(_scriptEngine.get()); DependencyManager::get<RecordingScriptingInterface>()->setScriptEngine(_scriptEngine);
// setup an Avatar for the script to use // setup an Avatar for the script to use
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>(); auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
connect(_scriptEngine.get(), SIGNAL(update(float)), scriptedAvatar.data(), SLOT(update(float)), Qt::ConnectionType::QueuedConnection); connect(_scriptEngine.data(), SIGNAL(update(float)),
scriptedAvatar.data(), SLOT(update(float)), Qt::ConnectionType::QueuedConnection);
scriptedAvatar->setForceFaceTrackerConnected(true); scriptedAvatar->setForceFaceTrackerConnected(true);
// call model URL setters with empty URLs so our avatar, if user, will have the default models // call model URL setters with empty URLs so our avatar, if user, will have the default models

View file

@ -88,7 +88,7 @@ private:
void encodeFrameOfZeros(QByteArray& encodedZeros); void encodeFrameOfZeros(QByteArray& encodedZeros);
void computeLoudness(const QByteArray* decodedBuffer, QSharedPointer<ScriptableAvatar>); void computeLoudness(const QByteArray* decodedBuffer, QSharedPointer<ScriptableAvatar>);
std::unique_ptr<ScriptEngine> _scriptEngine; QSharedPointer<ScriptEngine> _scriptEngine;
EntityEditPacketSender _entityEditSender; EntityEditPacketSender _entityEditSender;
EntityTreeHeadlessViewer _entityViewer; EntityTreeHeadlessViewer _entityViewer;

View file

@ -415,8 +415,7 @@ void EntityScriptServer::selectAudioFormat(const QString& selectedCodecName) {
void EntityScriptServer::resetEntitiesScriptEngine() { void EntityScriptServer::resetEntitiesScriptEngine() {
auto engineName = QString("about:Entities %1").arg(++_entitiesScriptEngineCount); auto engineName = QString("about:Entities %1").arg(++_entitiesScriptEngineCount);
auto newEngine = QSharedPointer<ScriptEngine>(new ScriptEngine(ScriptEngine::ENTITY_SERVER_SCRIPT, NO_SCRIPT, engineName), auto newEngine = scriptEngineFactory(ScriptEngine::ENTITY_SERVER_SCRIPT, NO_SCRIPT, engineName);
&ScriptEngine::deleteLater);
auto webSocketServerConstructorValue = newEngine->newFunction(WebSocketServerClass::constructor); auto webSocketServerConstructorValue = newEngine->newFunction(WebSocketServerClass::constructor);
newEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue); newEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
@ -437,11 +436,14 @@ void EntityScriptServer::resetEntitiesScriptEngine() {
newEngine->runInThread(); newEngine->runInThread();
DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(newEngine.data()); auto newEngineSP = qSharedPointerCast<EntitiesScriptEngineProvider>(newEngine);
DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(newEngineSP);
disconnect(_entitiesScriptEngine.data(), &ScriptEngine::entityScriptDetailsUpdated, this, &EntityScriptServer::updateEntityPPS); disconnect(_entitiesScriptEngine.data(), &ScriptEngine::entityScriptDetailsUpdated,
this, &EntityScriptServer::updateEntityPPS);
_entitiesScriptEngine.swap(newEngine); _entitiesScriptEngine.swap(newEngine);
connect(_entitiesScriptEngine.data(), &ScriptEngine::entityScriptDetailsUpdated, this, &EntityScriptServer::updateEntityPPS); connect(_entitiesScriptEngine.data(), &ScriptEngine::entityScriptDetailsUpdated,
this, &EntityScriptServer::updateEntityPPS);
} }

View file

@ -963,7 +963,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress); DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);
auto scriptEngines = DependencyManager::get<ScriptEngines>().data(); auto scriptEngines = DependencyManager::get<ScriptEngines>().data();
scriptEngines->registerScriptInitializer([this](ScriptEngine* engine){ scriptEngines->registerScriptInitializer([this](QSharedPointer<ScriptEngine> engine){
registerScriptEngineWithApplicationServices(engine); registerScriptEngineWithApplicationServices(engine);
}); });
@ -5930,7 +5930,7 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer
void Application::packetSent(quint64 length) { void Application::packetSent(quint64 length) {
} }
void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) { void Application::registerScriptEngineWithApplicationServices(QSharedPointer<ScriptEngine> scriptEngine) {
scriptEngine->setEmitScriptUpdatesFunction([this]() { scriptEngine->setEmitScriptUpdatesFunction([this]() {
SharedNodePointer entityServerNode = DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::EntityServer); SharedNodePointer entityServerNode = DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::EntityServer);
@ -5944,7 +5944,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
entityScriptingInterface->setEntityTree(getEntities()->getTree()); entityScriptingInterface->setEntityTree(getEntities()->getTree());
// give the script engine to the RecordingScriptingInterface for its callbacks // give the script engine to the RecordingScriptingInterface for its callbacks
DependencyManager::get<RecordingScriptingInterface>()->setScriptEngine(scriptEngine); DependencyManager::get<RecordingScriptingInterface>()->setScriptEngine(QSharedPointer<ScriptEngine>(scriptEngine));
if (property(hifi::properties::TEST).isValid()) { if (property(hifi::properties::TEST).isValid()) {
scriptEngine->registerGlobalObject("Test", TestScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("Test", TestScriptingInterface::getInstance());
@ -5965,29 +5965,31 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
ClipboardScriptingInterface* clipboardScriptable = new ClipboardScriptingInterface(); ClipboardScriptingInterface* clipboardScriptable = new ClipboardScriptingInterface();
scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable); scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable);
connect(scriptEngine, &ScriptEngine::finished, clipboardScriptable, &ClipboardScriptingInterface::deleteLater); connect(scriptEngine.data(), &ScriptEngine::finished, clipboardScriptable, &ClipboardScriptingInterface::deleteLater);
scriptEngine->registerGlobalObject("Overlays", &_overlays); scriptEngine->registerGlobalObject("Overlays", &_overlays);
qScriptRegisterMetaType(scriptEngine, OverlayPropertyResultToScriptValue, OverlayPropertyResultFromScriptValue); qScriptRegisterMetaType(scriptEngine.data(), OverlayPropertyResultToScriptValue, OverlayPropertyResultFromScriptValue);
qScriptRegisterMetaType(scriptEngine, RayToOverlayIntersectionResultToScriptValue, qScriptRegisterMetaType(scriptEngine.data(), RayToOverlayIntersectionResultToScriptValue,
RayToOverlayIntersectionResultFromScriptValue); RayToOverlayIntersectionResultFromScriptValue);
scriptEngine->registerGlobalObject("OffscreenFlags", DependencyManager::get<OffscreenUi>()->getFlags()); scriptEngine->registerGlobalObject("OffscreenFlags", DependencyManager::get<OffscreenUi>()->getFlags());
scriptEngine->registerGlobalObject("Desktop", DependencyManager::get<DesktopScriptingInterface>().data()); scriptEngine->registerGlobalObject("Desktop", DependencyManager::get<DesktopScriptingInterface>().data());
qScriptRegisterMetaType(scriptEngine, wrapperToScriptValue<ToolbarProxy>, wrapperFromScriptValue<ToolbarProxy>); qScriptRegisterMetaType(scriptEngine.data(), wrapperToScriptValue<ToolbarProxy>, wrapperFromScriptValue<ToolbarProxy>);
qScriptRegisterMetaType(scriptEngine, wrapperToScriptValue<ToolbarButtonProxy>, wrapperFromScriptValue<ToolbarButtonProxy>); qScriptRegisterMetaType(scriptEngine.data(),
wrapperToScriptValue<ToolbarButtonProxy>, wrapperFromScriptValue<ToolbarButtonProxy>);
scriptEngine->registerGlobalObject("Toolbars", DependencyManager::get<ToolbarScriptingInterface>().data()); scriptEngine->registerGlobalObject("Toolbars", DependencyManager::get<ToolbarScriptingInterface>().data());
qScriptRegisterMetaType(scriptEngine, wrapperToScriptValue<TabletProxy>, wrapperFromScriptValue<TabletProxy>); qScriptRegisterMetaType(scriptEngine.data(), wrapperToScriptValue<TabletProxy>, wrapperFromScriptValue<TabletProxy>);
qScriptRegisterMetaType(scriptEngine, wrapperToScriptValue<TabletButtonProxy>, wrapperFromScriptValue<TabletButtonProxy>); qScriptRegisterMetaType(scriptEngine.data(),
wrapperToScriptValue<TabletButtonProxy>, wrapperFromScriptValue<TabletButtonProxy>);
scriptEngine->registerGlobalObject("Tablet", DependencyManager::get<TabletScriptingInterface>().data()); scriptEngine->registerGlobalObject("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
auto toolbarScriptingInterface = DependencyManager::get<ToolbarScriptingInterface>().data();
DependencyManager::get<TabletScriptingInterface>().data()->setToolbarScriptingInterface(DependencyManager::get<ToolbarScriptingInterface>().data()); DependencyManager::get<TabletScriptingInterface>().data()->setToolbarScriptingInterface(toolbarScriptingInterface);
scriptEngine->registerGlobalObject("Window", DependencyManager::get<WindowScriptingInterface>().data()); scriptEngine->registerGlobalObject("Window", DependencyManager::get<WindowScriptingInterface>().data());
qScriptRegisterMetaType(scriptEngine, CustomPromptResultToScriptValue, CustomPromptResultFromScriptValue); qScriptRegisterMetaType(scriptEngine.data(), CustomPromptResultToScriptValue, CustomPromptResultFromScriptValue);
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter, scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
LocationScriptingInterface::locationSetter, "Window"); LocationScriptingInterface::locationSetter, "Window");
// register `location` on the global object. // register `location` on the global object.
@ -6019,7 +6021,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("DialogsManager", _dialogsManagerScriptingInterface); scriptEngine->registerGlobalObject("DialogsManager", _dialogsManagerScriptingInterface);
scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance());
qScriptRegisterMetaType(scriptEngine, DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue); qScriptRegisterMetaType(scriptEngine.data(), DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue);
scriptEngine->registerGlobalObject("FaceTracker", DependencyManager::get<DdeFaceTracker>().data()); scriptEngine->registerGlobalObject("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());
@ -6047,11 +6049,11 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("LimitlessSpeechRecognition", DependencyManager::get<LimitlessVoiceRecognitionScriptingInterface>().data()); scriptEngine->registerGlobalObject("LimitlessSpeechRecognition", DependencyManager::get<LimitlessVoiceRecognitionScriptingInterface>().data());
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
scriptEngine->registerGlobalObject("Steam", new SteamScriptingInterface(scriptEngine, steamClient.get())); scriptEngine->registerGlobalObject("Steam", new SteamScriptingInterface(scriptEngine.data(), steamClient.get()));
} }
auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>(); auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>();
scriptEngine->registerGlobalObject("Controller", scriptingInterface.data()); scriptEngine->registerGlobalObject("Controller", scriptingInterface.data());
UserInputMapper::registerControllerTypes(scriptEngine); UserInputMapper::registerControllerTypes(scriptEngine.data());
auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>(); auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>();
scriptEngine->registerGlobalObject("Recording", recordingInterface.data()); scriptEngine->registerGlobalObject("Recording", recordingInterface.data());
@ -6062,14 +6064,19 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("Selection", DependencyManager::get<SelectionScriptingInterface>().data()); scriptEngine->registerGlobalObject("Selection", DependencyManager::get<SelectionScriptingInterface>().data());
scriptEngine->registerGlobalObject("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data()); scriptEngine->registerGlobalObject("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
qScriptRegisterMetaType(scriptEngine, OverlayIDtoScriptValue, OverlayIDfromScriptValue); qScriptRegisterMetaType(scriptEngine.data(), OverlayIDtoScriptValue, OverlayIDfromScriptValue);
// connect this script engines printedMessage signal to the global ScriptEngines these various messages // connect this script engines printedMessage signal to the global ScriptEngines these various messages
connect(scriptEngine, &ScriptEngine::printedMessage, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onPrintedMessage); connect(scriptEngine.data(), &ScriptEngine::printedMessage,
connect(scriptEngine, &ScriptEngine::errorMessage, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onErrorMessage); DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onPrintedMessage);
connect(scriptEngine, &ScriptEngine::warningMessage, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onWarningMessage); connect(scriptEngine.data(), &ScriptEngine::errorMessage,
connect(scriptEngine, &ScriptEngine::infoMessage, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onInfoMessage); DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onErrorMessage);
connect(scriptEngine, &ScriptEngine::clearDebugWindow, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onClearDebugWindow); connect(scriptEngine.data(), &ScriptEngine::warningMessage,
DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onWarningMessage);
connect(scriptEngine.data(), &ScriptEngine::infoMessage,
DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onInfoMessage);
connect(scriptEngine.data(), &ScriptEngine::clearDebugWindow,
DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onClearDebugWindow);
} }

View file

@ -218,7 +218,7 @@ public:
NodeToOctreeSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; } NodeToOctreeSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; }
virtual controller::ScriptingInterface* getControllerScriptingInterface() { return _controllerScriptingInterface; } virtual controller::ScriptingInterface* getControllerScriptingInterface() { return _controllerScriptingInterface; }
virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) override; virtual void registerScriptEngineWithApplicationServices(QSharedPointer<ScriptEngine> scriptEngine) override;
virtual void copyCurrentViewFrustum(ViewFrustum& viewOut) const override { copyDisplayViewFrustum(viewOut); } virtual void copyCurrentViewFrustum(ViewFrustum& viewOut) const override { copyDisplayViewFrustum(viewOut); }
virtual QThread* getMainThread() override { return thread(); } virtual QThread* getMainThread() override { return thread(); }

View file

@ -262,7 +262,7 @@ void MyAvatar::setDominantHand(const QString& hand) {
} }
} }
void MyAvatar::registerMetaTypes(QScriptEngine* engine) { void MyAvatar::registerMetaTypes(QSharedPointer<BaseScriptEngine> engine) {
QScriptValue value = engine->newQObject(this, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); QScriptValue value = engine->newQObject(this, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects);
engine->globalObject().setProperty("MyAvatar", value); engine->globalObject().setProperty("MyAvatar", value);
@ -273,8 +273,8 @@ void MyAvatar::registerMetaTypes(QScriptEngine* engine) {
} }
engine->globalObject().setProperty("DriveKeys", driveKeys); engine->globalObject().setProperty("DriveKeys", driveKeys);
qScriptRegisterMetaType(engine, audioListenModeToScriptValue, audioListenModeFromScriptValue); qScriptRegisterMetaType(engine.data(), audioListenModeToScriptValue, audioListenModeFromScriptValue);
qScriptRegisterMetaType(engine, driveKeysToScriptValue, driveKeysFromScriptValue); qScriptRegisterMetaType(engine.data(), driveKeysToScriptValue, driveKeysFromScriptValue);
} }
void MyAvatar::setOrientationVar(const QVariant& newOrientationVar) { void MyAvatar::setOrientationVar(const QVariant& newOrientationVar) {

View file

@ -19,6 +19,7 @@
#include <SettingHandle.h> #include <SettingHandle.h>
#include <Rig.h> #include <Rig.h>
#include <Sound.h> #include <Sound.h>
#include <BaseScriptEngine.h>
#include <controllers/Pose.h> #include <controllers/Pose.h>
#include <controllers/Actions.h> #include <controllers/Actions.h>
@ -169,7 +170,7 @@ public:
~MyAvatar(); ~MyAvatar();
void instantiableAvatar() override {}; void instantiableAvatar() override {};
void registerMetaTypes(QScriptEngine* engine); void registerMetaTypes(QSharedPointer<BaseScriptEngine> engine);
virtual void simulateAttachments(float deltaTime) override; virtual void simulateAttachments(float deltaTime) override;

View file

@ -111,7 +111,7 @@ void JSConsole::setScriptEngine(const QSharedPointer<ScriptEngine>& scriptEngin
// if scriptEngine is NULL then create one and keep track of it using _ownScriptEngine // if scriptEngine is NULL then create one and keep track of it using _ownScriptEngine
if (scriptEngine.isNull()) { if (scriptEngine.isNull()) {
_scriptEngine = QSharedPointer<ScriptEngine>(DependencyManager::get<ScriptEngines>()->loadScript(_consoleFileName, false), &QObject::deleteLater); _scriptEngine = DependencyManager::get<ScriptEngines>()->loadScript(_consoleFileName, false);
} else { } else {
_scriptEngine = scriptEngine; _scriptEngine = scriptEngine;
} }

View file

@ -24,7 +24,7 @@ TestingDialog::TestingDialog(QWidget* parent) :
_console->setFixedHeight(TESTING_CONSOLE_HEIGHT); _console->setFixedHeight(TESTING_CONSOLE_HEIGHT);
auto _engines = DependencyManager::get<ScriptEngines>(); auto _engines = DependencyManager::get<ScriptEngines>();
_engine.reset(_engines->loadScript(qApp->applicationDirPath() + testRunnerRelativePath)); _engine = _engines->loadScript(qApp->applicationDirPath() + testRunnerRelativePath);
_console->setScriptEngine(_engine); _console->setScriptEngine(_engine);
connect(_engine.data(), &ScriptEngine::finished, this, &TestingDialog::onTestingFinished); connect(_engine.data(), &ScriptEngine::finished, this, &TestingDialog::onTestingFinished);
} }

View file

@ -77,38 +77,14 @@ render::ItemID EntityTreeRenderer::renderableIdForEntityId(const EntityItemID& i
int EntityTreeRenderer::_entitiesScriptEngineCount = 0; int EntityTreeRenderer::_entitiesScriptEngineCount = 0;
void entitiesScriptEngineDeleter(ScriptEngine* engine) {
class WaitRunnable : public QRunnable {
public:
WaitRunnable(ScriptEngine* engine) : _engine(engine) {}
virtual void run() override {
_engine->waitTillDoneRunning();
_engine->deleteLater();
}
private:
ScriptEngine* _engine;
};
// Wait for the scripting thread from the thread pool to avoid hanging the main thread
auto threadPool = QThreadPool::globalInstance();
if (threadPool) {
threadPool->start(new WaitRunnable(engine));
} else {
delete engine;
}
}
void EntityTreeRenderer::resetEntitiesScriptEngine() { void EntityTreeRenderer::resetEntitiesScriptEngine() {
// Keep a ref to oldEngine until newEngine is ready so EntityScriptingInterface has something to use
auto oldEngine = _entitiesScriptEngine; auto oldEngine = _entitiesScriptEngine;
_entitiesScriptEngine = scriptEngineFactory(ScriptEngine::ENTITY_CLIENT_SCRIPT, NO_SCRIPT,
auto newEngine = new ScriptEngine(ScriptEngine::ENTITY_CLIENT_SCRIPT, NO_SCRIPT, QString("about:Entities %1").arg(++_entitiesScriptEngineCount)); QString("about:Entities %1").arg(++_entitiesScriptEngineCount));
_entitiesScriptEngine = QSharedPointer<ScriptEngine>(newEngine, entitiesScriptEngineDeleter); _scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine);
_scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine.data());
_entitiesScriptEngine->runInThread(); _entitiesScriptEngine->runInThread();
DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(_entitiesScriptEngine.data()); auto entitiesScriptEngineProvider = qSharedPointerCast<EntitiesScriptEngineProvider>(_entitiesScriptEngine);
DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(entitiesScriptEngineProvider);
} }
void EntityTreeRenderer::clear() { void EntityTreeRenderer::clear() {

View file

@ -539,7 +539,7 @@ void EntityScriptingInterface::deleteEntity(QUuid id) {
} }
} }
void EntityScriptingInterface::setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine) { void EntityScriptingInterface::setEntitiesScriptEngine(QSharedPointer<EntitiesScriptEngineProvider> engine) {
std::lock_guard<std::recursive_mutex> lock(_entitiesScriptEngineLock); std::lock_guard<std::recursive_mutex> lock(_entitiesScriptEngineLock);
_entitiesScriptEngine = engine; _entitiesScriptEngine = engine;
} }
@ -749,7 +749,7 @@ bool EntityPropertyMetadataRequest::script(EntityItemID entityID, QScriptValue h
request->deleteLater(); request->deleteLater();
}); });
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
entityScriptingInterface->withEntitiesScriptEngine([&](EntitiesScriptEngineProvider* entitiesScriptEngine) { entityScriptingInterface->withEntitiesScriptEngine([&](QSharedPointer<EntitiesScriptEngineProvider> entitiesScriptEngine) {
if (entitiesScriptEngine) { if (entitiesScriptEngine) {
request->setFuture(entitiesScriptEngine->getLocalEntityScriptDetails(entityID)); request->setFuture(entitiesScriptEngine->getLocalEntityScriptDetails(entityID));
} }

View file

@ -99,7 +99,7 @@ public:
void setEntityTree(EntityTreePointer modelTree); void setEntityTree(EntityTreePointer modelTree);
EntityTreePointer getEntityTree() { return _entityTree; } EntityTreePointer getEntityTree() { return _entityTree; }
void setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine); void setEntitiesScriptEngine(QSharedPointer<EntitiesScriptEngineProvider> engine);
float calculateCost(float mass, float oldVelocity, float newVelocity); float calculateCost(float mass, float oldVelocity, float newVelocity);
void resetActivityTracking(); void resetActivityTracking();
@ -405,7 +405,7 @@ signals:
void webEventReceived(const EntityItemID& entityItemID, const QVariant& message); void webEventReceived(const EntityItemID& entityItemID, const QVariant& message);
protected: protected:
void withEntitiesScriptEngine(std::function<void(EntitiesScriptEngineProvider*)> function) { void withEntitiesScriptEngine(std::function<void(QSharedPointer<EntitiesScriptEngineProvider>)> function) {
std::lock_guard<std::recursive_mutex> lock(_entitiesScriptEngineLock); std::lock_guard<std::recursive_mutex> lock(_entitiesScriptEngineLock);
function(_entitiesScriptEngine); function(_entitiesScriptEngine);
}; };
@ -427,7 +427,7 @@ private:
EntityTreePointer _entityTree; EntityTreePointer _entityTree;
std::recursive_mutex _entitiesScriptEngineLock; std::recursive_mutex _entitiesScriptEngineLock;
EntitiesScriptEngineProvider* _entitiesScriptEngine { nullptr }; QSharedPointer<EntitiesScriptEngineProvider> _entitiesScriptEngine;
bool _bidOnSimulationOwnership { false }; bool _bidOnSimulationOwnership { false };
float _currentAvatarEnergy = { FLT_MAX }; float _currentAvatarEnergy = { FLT_MAX };

View file

@ -18,7 +18,7 @@ class ScriptEngine;
class AbstractScriptingServicesInterface { class AbstractScriptingServicesInterface {
public: public:
/// Registers application specific services with a script engine. /// Registers application specific services with a script engine.
virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) = 0; virtual void registerScriptEngineWithApplicationServices(QSharedPointer<ScriptEngine> scriptEngine) = 0;
}; };

View file

@ -14,6 +14,7 @@
#include <QtCore/QObject> #include <QtCore/QObject>
#include <BaseScriptEngine.h>
#include <DependencyManager.h> #include <DependencyManager.h>
#include <recording/ClipCache.h> #include <recording/ClipCache.h>
#include <recording/Forward.h> #include <recording/Forward.h>
@ -28,7 +29,7 @@ class RecordingScriptingInterface : public QObject, public Dependency {
public: public:
RecordingScriptingInterface(); RecordingScriptingInterface();
void setScriptEngine(QScriptEngine* scriptEngine) { _scriptEngine = scriptEngine; } void setScriptEngine(QSharedPointer<BaseScriptEngine> scriptEngine) { _scriptEngine = scriptEngine; }
public slots: public slots:
void loadRecording(const QString& url, QScriptValue callback = QScriptValue()); void loadRecording(const QString& url, QScriptValue callback = QScriptValue());
@ -85,7 +86,7 @@ protected:
Flag _useSkeletonModel { false }; Flag _useSkeletonModel { false };
recording::ClipPointer _lastClip; recording::ClipPointer _lastClip;
QScriptEngine* _scriptEngine; QSharedPointer<BaseScriptEngine> _scriptEngine;
QSet<recording::NetworkClipLoaderPointer> _clipLoaders; QSet<recording::NetworkClipLoaderPointer> _clipLoaders;
}; };

View file

@ -153,6 +153,15 @@ QString ScriptEngine::logException(const QScriptValue& exception) {
return message; return message;
} }
QSharedPointer<ScriptEngine> scriptEngineFactory(ScriptEngine::Context context,
const QString& scriptContents,
const QString& fileNameString) {
ScriptEngine* engine = new ScriptEngine(context, scriptContents, fileNameString);
QSharedPointer<ScriptEngine> engineSP = QSharedPointer<ScriptEngine>(engine);
DependencyManager::get<ScriptEngines>()->addScriptEngine(qSharedPointerCast<ScriptEngine>(engineSP));
return engineSP;
}
int ScriptEngine::processLevelMaxRetries { ScriptRequest::MAX_RETRIES }; int ScriptEngine::processLevelMaxRetries { ScriptRequest::MAX_RETRIES };
ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const QString& fileNameString) : ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const QString& fileNameString) :
BaseScriptEngine(), BaseScriptEngine(),
@ -160,10 +169,10 @@ ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const
_scriptContents(scriptContents), _scriptContents(scriptContents),
_timerFunctionMap(), _timerFunctionMap(),
_fileNameString(fileNameString), _fileNameString(fileNameString),
_arrayBufferClass(new ArrayBufferClass(this)) _arrayBufferClass(new ArrayBufferClass(this)),
// don't delete `ScriptEngines` until all `ScriptEngine`s are gone
_scriptEngines(DependencyManager::get<ScriptEngines>())
{ {
DependencyManager::get<ScriptEngines>()->addScriptEngine(this);
connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) { connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) {
if (hasUncaughtException()) { if (hasUncaughtException()) {
// the engine's uncaughtException() seems to produce much better stack traces here // the engine's uncaughtException() seems to produce much better stack traces here
@ -208,22 +217,9 @@ QString ScriptEngine::getContext() const {
} }
ScriptEngine::~ScriptEngine() { ScriptEngine::~ScriptEngine() {
// FIXME: are these scriptInfoMessage/scriptWarningMessage segfaulting anybody else at app shutdown?
#if !defined(Q_OS_LINUX)
scriptInfoMessage("Script Engine shutting down:" + getFilename());
#else
qCDebug(scriptengine) << "~ScriptEngine()" << this;
#endif
auto scriptEngines = DependencyManager::get<ScriptEngines>(); auto scriptEngines = DependencyManager::get<ScriptEngines>();
if (scriptEngines) { if (scriptEngines) {
scriptEngines->removeScriptEngine(this); scriptEngines->removeScriptEngine(qSharedPointerCast<ScriptEngine>(sharedFromThis()));
} else {
#if !defined(Q_OS_LINUX)
scriptWarningMessage("Script destroyed after ScriptEngines!");
#else
qCWarning(scriptengine) << ("Script destroyed after ScriptEngines!");
#endif
} }
} }
@ -294,7 +290,7 @@ void ScriptEngine::runDebuggable() {
stopAllTimers(); // make sure all our timers are stopped if the script is ending stopAllTimers(); // make sure all our timers are stopped if the script is ending
emit scriptEnding(); emit scriptEnding();
emit finished(_fileNameString, this); emit finished(_fileNameString, qSharedPointerCast<ScriptEngine>(sharedFromThis()));
_isRunning = false; _isRunning = false;
emit runningStateChanged(); emit runningStateChanged();
@ -503,7 +499,8 @@ static void animVarMapFromScriptValue(const QScriptValue& value, AnimVariantMap&
parameters.animVariantMapFromScriptValue(value); parameters.animVariantMapFromScriptValue(value);
} }
// ... while these two are not. But none of the four are ever used. // ... while these two are not. But none of the four are ever used.
static QScriptValue resultHandlerToScriptValue(QScriptEngine* engine, const AnimVariantResultHandler& resultHandler) { static QScriptValue resultHandlerToScriptValue(QScriptEngine* engine,
const AnimVariantResultHandler& resultHandler) {
qCCritical(scriptengine) << "Attempt to marshall result handler to javascript"; qCCritical(scriptengine) << "Attempt to marshall result handler to javascript";
assert(false); assert(false);
return QScriptValue(); return QScriptValue();
@ -516,7 +513,8 @@ static void resultHandlerFromScriptValue(const QScriptValue& value, AnimVariantR
// Templated qScriptRegisterMetaType fails to compile with raw pointers // Templated qScriptRegisterMetaType fails to compile with raw pointers
using ScriptableResourceRawPtr = ScriptableResource*; using ScriptableResourceRawPtr = ScriptableResource*;
static QScriptValue scriptableResourceToScriptValue(QScriptEngine* engine, const ScriptableResourceRawPtr& resource) { static QScriptValue scriptableResourceToScriptValue(QScriptEngine* engine,
const ScriptableResourceRawPtr& resource) {
// The first script to encounter this resource will track its memory. // The first script to encounter this resource will track its memory.
// In this way, it will be more likely to GC. // In this way, it will be more likely to GC.
// This fails in the case that the resource is used across many scripts, but // This fails in the case that the resource is used across many scripts, but
@ -539,11 +537,11 @@ static void scriptableResourceFromScriptValue(const QScriptValue& value, Scripta
resource = static_cast<ScriptableResourceRawPtr>(value.toQObject()); resource = static_cast<ScriptableResourceRawPtr>(value.toQObject());
} }
static QScriptValue createScriptableResourcePrototype(QScriptEngine* engine) { static QScriptValue createScriptableResourcePrototype(QSharedPointer<ScriptEngine> engine) {
auto prototype = engine->newObject(); auto prototype = engine->newObject();
// Expose enum State to JS/QML via properties // Expose enum State to JS/QML via properties
QObject* state = new QObject(engine); QObject* state = new QObject(engine.data());
state->setObjectName("ResourceState"); state->setObjectName("ResourceState");
auto metaEnum = QMetaEnum::fromType<ScriptableResource::State>(); auto metaEnum = QMetaEnum::fromType<ScriptableResource::State>();
for (int i = 0; i < metaEnum.keyCount(); ++i) { for (int i = 0; i < metaEnum.keyCount(); ++i) {
@ -693,7 +691,7 @@ void ScriptEngine::init() {
qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue); qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue);
// Scriptable cache access // Scriptable cache access
auto resourcePrototype = createScriptableResourcePrototype(this); auto resourcePrototype = createScriptableResourcePrototype(qSharedPointerCast<ScriptEngine>(sharedFromThis()));
globalObject().setProperty("Resource", resourcePrototype); globalObject().setProperty("Resource", resourcePrototype);
setDefaultPrototype(qMetaTypeId<ScriptableResource*>(), resourcePrototype); setDefaultPrototype(qMetaTypeId<ScriptableResource*>(), resourcePrototype);
qScriptRegisterMetaType(this, scriptableResourceToScriptValue, scriptableResourceFromScriptValue); qScriptRegisterMetaType(this, scriptableResourceToScriptValue, scriptableResourceFromScriptValue);
@ -1178,7 +1176,7 @@ void ScriptEngine::run() {
} }
} }
emit finished(_fileNameString, this); emit finished(_fileNameString, qSharedPointerCast<ScriptEngine>(sharedFromThis()));
_isRunning = false; _isRunning = false;
emit runningStateChanged(); emit runningStateChanged();

View file

@ -53,6 +53,8 @@ static const int SCRIPT_FPS = 60;
static const int DEFAULT_MAX_ENTITY_PPS = 9000; static const int DEFAULT_MAX_ENTITY_PPS = 9000;
static const int DEFAULT_ENTITY_PPS_PER_SCRIPT = 900; static const int DEFAULT_ENTITY_PPS_PER_SCRIPT = 900;
class ScriptEngines;
class CallbackData { class CallbackData {
public: public:
QScriptValue function; QScriptValue function;
@ -242,7 +244,7 @@ signals:
void errorLoadingScript(const QString& scriptFilename); void errorLoadingScript(const QString& scriptFilename);
void update(float deltaTime); void update(float deltaTime);
void scriptEnding(); void scriptEnding();
void finished(const QString& fileNameString, ScriptEngine* engine); void finished(const QString& fileNameString, QSharedPointer<ScriptEngine>);
void cleanupMenuItem(const QString& menuItemString); void cleanupMenuItem(const QString& menuItemString);
void printedMessage(const QString& message, const QString& scriptName); void printedMessage(const QString& message, const QString& scriptName);
void errorMessage(const QString& message, const QString& scriptName); void errorMessage(const QString& message, const QString& scriptName);
@ -328,6 +330,12 @@ protected:
static const QString _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS; static const QString _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS;
Setting::Handle<bool> _enableExtendedJSExceptions { _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS, true }; Setting::Handle<bool> _enableExtendedJSExceptions { _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS, true };
QSharedPointer<ScriptEngines> _scriptEngines;
}; };
QSharedPointer<ScriptEngine> scriptEngineFactory(ScriptEngine::Context context,
const QString& scriptContents,
const QString& fileNameString);
#endif // hifi_ScriptEngine_h #endif // hifi_ScriptEngine_h

View file

@ -137,16 +137,14 @@ void ScriptEngines::registerScriptInitializer(ScriptInitializer initializer) {
_scriptInitializers.push_back(initializer); _scriptInitializers.push_back(initializer);
} }
void ScriptEngines::addScriptEngine(ScriptEngine* engine) { void ScriptEngines::addScriptEngine(QSharedPointer<ScriptEngine> engine) {
if (_isStopped) { if (!_isStopped) {
engine->deleteLater();
} else {
QMutexLocker locker(&_allScriptsMutex); QMutexLocker locker(&_allScriptsMutex);
_allKnownScriptEngines.insert(engine); _allKnownScriptEngines.insert(engine);
} }
} }
void ScriptEngines::removeScriptEngine(ScriptEngine* engine) { void ScriptEngines::removeScriptEngine(QSharedPointer<ScriptEngine> engine) {
// If we're not already in the middle of stopping all scripts, then we should remove ourselves // If we're not already in the middle of stopping all scripts, then we should remove ourselves
// from the list of running scripts. We don't do this if we're in the process of stopping all scripts // from the list of running scripts. We don't do this if we're in the process of stopping all scripts
// because that method removes scripts from its list as it iterates them // because that method removes scripts from its list as it iterates them
@ -161,9 +159,9 @@ void ScriptEngines::shutdownScripting() {
QMutexLocker locker(&_allScriptsMutex); QMutexLocker locker(&_allScriptsMutex);
qCDebug(scriptengine) << "Stopping all scripts.... currently known scripts:" << _allKnownScriptEngines.size(); qCDebug(scriptengine) << "Stopping all scripts.... currently known scripts:" << _allKnownScriptEngines.size();
QMutableSetIterator<ScriptEngine*> i(_allKnownScriptEngines); QMutableSetIterator<QSharedPointer<ScriptEngine>> i(_allKnownScriptEngines);
while (i.hasNext()) { while (i.hasNext()) {
ScriptEngine* scriptEngine = i.next(); QSharedPointer<ScriptEngine> scriptEngine = i.next();
QString scriptName = scriptEngine->getFilename(); QString scriptName = scriptEngine->getFilename();
// NOTE: typically all script engines are running. But there's at least one known exception to this, the // NOTE: typically all script engines are running. But there's at least one known exception to this, the
@ -187,13 +185,10 @@ void ScriptEngines::shutdownScripting() {
qCDebug(scriptengine) << "waiting on script:" << scriptName; qCDebug(scriptengine) << "waiting on script:" << scriptName;
scriptEngine->waitTillDoneRunning(); scriptEngine->waitTillDoneRunning();
qCDebug(scriptengine) << "done waiting on script:" << scriptName; qCDebug(scriptengine) << "done waiting on script:" << scriptName;
}
scriptEngine->deleteLater();
// Once the script is stopped, we can remove it from our set // Once the script is stopped, we can remove it from our set
i.remove(); i.remove();
} }
}
qCDebug(scriptengine) << "DONE Stopping all scripts...."; qCDebug(scriptengine) << "DONE Stopping all scripts....";
} }
@ -369,9 +364,9 @@ void ScriptEngines::stopAllScripts(bool restart) {
_isReloading = true; _isReloading = true;
} }
for (QHash<QUrl, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin(); for (QHash<QUrl, QSharedPointer<ScriptEngine>>::const_iterator it = _scriptEnginesHash.constBegin();
it != _scriptEnginesHash.constEnd(); it++) { it != _scriptEnginesHash.constEnd(); it++) {
ScriptEngine *scriptEngine = it.value(); QSharedPointer<ScriptEngine> scriptEngine = it.value();
// skip already stopped scripts // skip already stopped scripts
if (scriptEngine->isFinished() || scriptEngine->isStopping()) { if (scriptEngine->isFinished() || scriptEngine->isStopping()) {
continue; continue;
@ -417,11 +412,12 @@ bool ScriptEngines::stopScript(const QString& rawScriptURL, bool restart) {
QReadLocker lock(&_scriptEnginesHashLock); QReadLocker lock(&_scriptEnginesHashLock);
if (_scriptEnginesHash.contains(scriptURL)) { if (_scriptEnginesHash.contains(scriptURL)) {
ScriptEngine* scriptEngine = _scriptEnginesHash[scriptURL]; QSharedPointer<ScriptEngine> scriptEngine = _scriptEnginesHash[scriptURL];
if (restart) { if (restart) {
auto scriptCache = DependencyManager::get<ScriptCache>(); auto scriptCache = DependencyManager::get<ScriptCache>();
scriptCache->deleteScript(scriptURL); scriptCache->deleteScript(scriptURL);
connect(scriptEngine, &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) { connect(scriptEngine.data(), &ScriptEngine::finished,
this, [this](QString scriptName, QSharedPointer<ScriptEngine> engine) {
reloadScript(scriptName); reloadScript(scriptName);
}); });
} }
@ -449,11 +445,11 @@ void ScriptEngines::reloadAllScripts() {
stopAllScripts(true); stopAllScripts(true);
} }
ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserLoaded, bool loadScriptFromEditor, QSharedPointer<ScriptEngine> ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserLoaded, bool loadScriptFromEditor,
bool activateMainWindow, bool reload) { bool activateMainWindow, bool reload) {
if (thread() != QThread::currentThread()) { if (thread() != QThread::currentThread()) {
ScriptEngine* result { nullptr }; QSharedPointer<ScriptEngine> result { nullptr };
BLOCKING_INVOKE_METHOD(this, "loadScript", Q_RETURN_ARG(ScriptEngine*, result), BLOCKING_INVOKE_METHOD(this, "loadScript", Q_RETURN_ARG(QSharedPointer<ScriptEngine>, result),
Q_ARG(QUrl, scriptFilename), Q_ARG(QUrl, scriptFilename),
Q_ARG(bool, isUserLoaded), Q_ARG(bool, isUserLoaded),
Q_ARG(bool, loadScriptFromEditor), Q_ARG(bool, loadScriptFromEditor),
@ -479,19 +475,16 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL
return scriptEngine; return scriptEngine;
} }
scriptEngine = new ScriptEngine(_context, NO_SCRIPT, "about:" + scriptFilename.fileName()); scriptEngine = QSharedPointer<ScriptEngine>(new ScriptEngine(_context, NO_SCRIPT, "about:" + scriptFilename.fileName()));
addScriptEngine(scriptEngine);
scriptEngine->setUserLoaded(isUserLoaded); scriptEngine->setUserLoaded(isUserLoaded);
connect(scriptEngine, &ScriptEngine::doneRunning, this, [scriptEngine] {
scriptEngine->deleteLater();
}, Qt::QueuedConnection);
if (scriptFilename.isEmpty() || !scriptUrl.isValid()) { if (scriptFilename.isEmpty() || !scriptUrl.isValid()) {
launchScriptEngine(scriptEngine); launchScriptEngine(scriptEngine);
} else { } else {
// connect to the appropriate signals of this script engine // connect to the appropriate signals of this script engine
connect(scriptEngine, &ScriptEngine::scriptLoaded, this, &ScriptEngines::onScriptEngineLoaded); connect(scriptEngine.data(), &ScriptEngine::scriptLoaded, this, &ScriptEngines::onScriptEngineLoaded);
connect(scriptEngine, &ScriptEngine::errorLoadingScript, this, &ScriptEngines::onScriptEngineError); connect(scriptEngine.data(), &ScriptEngine::errorLoadingScript, this, &ScriptEngines::onScriptEngineError);
// get the script engine object to load the script at the designated script URL // get the script engine object to load the script at the designated script URL
scriptEngine->loadURL(scriptUrl, reload); scriptEngine->loadURL(scriptUrl, reload);
@ -500,8 +493,8 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL
return scriptEngine; return scriptEngine;
} }
ScriptEngine* ScriptEngines::getScriptEngine(const QUrl& rawScriptURL) { QSharedPointer<ScriptEngine> ScriptEngines::getScriptEngine(const QUrl& rawScriptURL) {
ScriptEngine* result = nullptr; QSharedPointer<ScriptEngine> result;
{ {
QReadLocker lock(&_scriptEnginesHashLock); QReadLocker lock(&_scriptEnginesHashLock);
const QUrl scriptURL = normalizeScriptURL(rawScriptURL); const QUrl scriptURL = normalizeScriptURL(rawScriptURL);
@ -516,7 +509,8 @@ ScriptEngine* ScriptEngines::getScriptEngine(const QUrl& rawScriptURL) {
// FIXME - change to new version of ScriptCache loading notification // FIXME - change to new version of ScriptCache loading notification
void ScriptEngines::onScriptEngineLoaded(const QString& rawScriptURL) { void ScriptEngines::onScriptEngineLoaded(const QString& rawScriptURL) {
UserActivityLogger::getInstance().loadedScript(rawScriptURL); UserActivityLogger::getInstance().loadedScript(rawScriptURL);
ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(sender()); QSharedPointer<BaseScriptEngine> baseScriptEngine = qobject_cast<ScriptEngine*>(sender())->sharedFromThis();
QSharedPointer<ScriptEngine> scriptEngine = qSharedPointerCast<ScriptEngine>(baseScriptEngine);
launchScriptEngine(scriptEngine); launchScriptEngine(scriptEngine);
@ -532,12 +526,12 @@ void ScriptEngines::onScriptEngineLoaded(const QString& rawScriptURL) {
emit scriptCountChanged(); emit scriptCountChanged();
} }
void ScriptEngines::launchScriptEngine(ScriptEngine* scriptEngine) { void ScriptEngines::launchScriptEngine(QSharedPointer<ScriptEngine> scriptEngine) {
connect(scriptEngine, &ScriptEngine::finished, this, &ScriptEngines::onScriptFinished, Qt::DirectConnection); connect(scriptEngine.data(), &ScriptEngine::finished, this, &ScriptEngines::onScriptFinished, Qt::DirectConnection);
connect(scriptEngine, &ScriptEngine::loadScript, [&](const QString& scriptName, bool userLoaded) { connect(scriptEngine.data(), &ScriptEngine::loadScript, [&](const QString& scriptName, bool userLoaded) {
loadScript(scriptName, userLoaded); loadScript(scriptName, userLoaded);
}); });
connect(scriptEngine, &ScriptEngine::reloadScript, [&](const QString& scriptName, bool userLoaded) { connect(scriptEngine.data(), &ScriptEngine::reloadScript, [&](const QString& scriptName, bool userLoaded) {
loadScript(scriptName, userLoaded, false, false, true); loadScript(scriptName, userLoaded, false, false, true);
}); });
@ -558,7 +552,7 @@ void ScriptEngines::launchScriptEngine(ScriptEngine* scriptEngine) {
} }
} }
void ScriptEngines::onScriptFinished(const QString& rawScriptURL, ScriptEngine* engine) { void ScriptEngines::onScriptFinished(const QString& rawScriptURL, QSharedPointer<ScriptEngine> engine) {
bool removed = false; bool removed = false;
{ {
QWriteLocker lock(&_scriptEnginesHashLock); QWriteLocker lock(&_scriptEnginesHashLock);

View file

@ -33,7 +33,7 @@ class ScriptEngines : public QObject, public Dependency {
Q_PROPERTY(ScriptsModelFilter* scriptsModelFilter READ scriptsModelFilter CONSTANT) Q_PROPERTY(ScriptsModelFilter* scriptsModelFilter READ scriptsModelFilter CONSTANT)
public: public:
using ScriptInitializer = std::function<void(ScriptEngine*)>; using ScriptInitializer = std::function<void(QSharedPointer<ScriptEngine>)>;
ScriptEngines(ScriptEngine::Context context); ScriptEngines(ScriptEngine::Context context);
void registerScriptInitializer(ScriptInitializer initializer); void registerScriptInitializer(ScriptInitializer initializer);
@ -45,7 +45,7 @@ public:
void loadDefaultScripts(); void loadDefaultScripts();
void setScriptsLocation(const QString& scriptsLocation); void setScriptsLocation(const QString& scriptsLocation);
QStringList getRunningScripts(); QStringList getRunningScripts();
ScriptEngine* getScriptEngine(const QUrl& scriptHash); QSharedPointer<ScriptEngine> getScriptEngine(const QUrl& scriptHash);
ScriptsModel* scriptsModel() { return &_scriptsModel; }; ScriptsModel* scriptsModel() { return &_scriptsModel; };
ScriptsModelFilter* scriptsModelFilter() { return &_scriptsModelFilter; }; ScriptsModelFilter* scriptsModelFilter() { return &_scriptsModelFilter; };
@ -53,7 +53,7 @@ public:
QString getDefaultScriptsLocation() const; QString getDefaultScriptsLocation() const;
Q_INVOKABLE void loadOneScript(const QString& scriptFilename); Q_INVOKABLE void loadOneScript(const QString& scriptFilename);
Q_INVOKABLE ScriptEngine* loadScript(const QUrl& scriptFilename = QString(), Q_INVOKABLE QSharedPointer<ScriptEngine> loadScript(const QUrl& scriptFilename = QString(),
bool isUserLoaded = true, bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false); bool isUserLoaded = true, bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false);
Q_INVOKABLE bool stopScript(const QString& scriptHash, bool restart = false); Q_INVOKABLE bool stopScript(const QString& scriptHash, bool restart = false);
@ -72,6 +72,8 @@ public:
void shutdownScripting(); void shutdownScripting();
bool isStopped() const { return _isStopped; } bool isStopped() const { return _isStopped; }
void addScriptEngine(QSharedPointer<ScriptEngine>);
signals: signals:
void scriptCountChanged(); void scriptCountChanged();
void scriptsReloading(); void scriptsReloading();
@ -92,22 +94,21 @@ public slots:
void onClearDebugWindow(); void onClearDebugWindow();
protected slots: protected slots:
void onScriptFinished(const QString& fileNameString, ScriptEngine* engine); void onScriptFinished(const QString& fileNameString, QSharedPointer<ScriptEngine> engine);
protected: protected:
friend class ScriptEngine; friend class ScriptEngine;
void reloadScript(const QString& scriptName) { loadScript(scriptName, true, false, false, true); } void reloadScript(const QString& scriptName) { loadScript(scriptName, true, false, false, true); }
void addScriptEngine(ScriptEngine* engine); void removeScriptEngine(QSharedPointer<ScriptEngine>);
void removeScriptEngine(ScriptEngine* engine);
void onScriptEngineLoaded(const QString& scriptFilename); void onScriptEngineLoaded(const QString& scriptFilename);
void onScriptEngineError(const QString& scriptFilename); void onScriptEngineError(const QString& scriptFilename);
void launchScriptEngine(ScriptEngine* engine); void launchScriptEngine(QSharedPointer<ScriptEngine>);
ScriptEngine::Context _context; ScriptEngine::Context _context;
QReadWriteLock _scriptEnginesHashLock; QReadWriteLock _scriptEnginesHashLock;
QHash<QUrl, ScriptEngine*> _scriptEnginesHash; QHash<QUrl, QSharedPointer<ScriptEngine>> _scriptEnginesHash;
QSet<ScriptEngine*> _allKnownScriptEngines; QSet<QSharedPointer<ScriptEngine>> _allKnownScriptEngines;
QMutex _allScriptsMutex; QMutex _allScriptsMutex;
std::list<ScriptInitializer> _scriptInitializers; std::list<ScriptInitializer> _scriptInitializers;
mutable Setting::Handle<QString> _scriptsLocationHandle; mutable Setting::Handle<QString> _scriptsLocationHandle;