History

first cut at cleaning up ScriptEngine class

Brad Hefta-Gaub committed 11 years ago

Commit f85cb2c1 parent f51f17b6 files · +276 −160Browse files

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

Unified · Split

libraries/script-engine/src/ScriptEngine.cpp+143−65
@@ -44,6 +44,8 @@ · 43 unmodified lines
4444
4545 #include "MIDIEvent.h"
4646
47+Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature)
48+static int functionSignatureMetaID = qRegisterMetaType<QScriptEngine::FunctionSignature>();
4749
4850 static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){
4951 QString message = "";
@@ -97,7 +99,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam · 47 unmodified lines
9799 _controllerScriptingInterface(controllerScriptingInterface),
98100 _wantSignals(wantSignals),
99101 _avatarData(NULL),
100− _scriptName(),
101102 _fileNameString(fileNameString),
102103 _quatLibrary(),
103104 _vec3Library(),
@@ -122,6 +123,27 @@ ScriptEngine::~ScriptEngine() { · 18 unmodified lines
122123 }
123124 }
124125
126+void ScriptEngine::runInThread() {
127+ QThread* workerThread = new QThread(this);
128+ QString scriptEngineName = QString("Script Thread:") + getFilename();
129+ workerThread->setObjectName(scriptEngineName);
130+
131+ // when the worker thread is started, call our engine's run..
132+ connect(workerThread, &QThread::started, this, &ScriptEngine::run);
133+
134+ // when the thread is terminated, add both scriptEngine and thread to the deleteLater queue
135+ connect(this, &ScriptEngine::doneRunning, this, &ScriptEngine::deleteLater);
136+ connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
137+
138+ // tell the thread to stop when the script engine is done
139+ connect(this, &ScriptEngine::destroyed, workerThread, &QThread::quit);
140+
141+ moveToThread(workerThread);
142+
143+ // Starts an event loop, and emits workerThread->started()
144+ workerThread->start();
145+}
146+
125147 QSet<ScriptEngine*> ScriptEngine::_allKnownScriptEngines;
126148 QMutex ScriptEngine::_allScriptsMutex;
127149 bool ScriptEngine::_stoppingAllScripts = false;
@@ -180,8 +202,6 @@ void ScriptEngine::stopAllScripts(QObject* application) { · 52 unmodified lines
180202
181203
182204 void ScriptEngine::waitTillDoneRunning() {
183− QString scriptName = getFilename();
184−
185205 // If the script never started running or finished running before we got here, we don't need to wait for it
186206 if (_isRunning) {
187207
@@ -244,14 +264,6 @@ void ScriptEngine::setAvatarData(AvatarData* avatarData, const QString& objectNa · 56 unmodified lines
244264 registerGlobalObject(objectName, _avatarData);
245265 }
246266
247−void ScriptEngine::setAvatarHashMap(AvatarHashMap* avatarHashMap, const QString& objectName) {
248− // remove the old Avatar property, if it exists
249− globalObject().setProperty(objectName, QScriptValue());
250−
251− // give the script engine the new avatar hash map
252− registerGlobalObject(objectName, avatarHashMap);
253−}
254−
255267 bool ScriptEngine::setScriptContents(const QString& scriptContents, const QString& fileNameString) {
256268 if (_isRunning) {
257269 return false;
@@ -261,7 +273,10 @@ bool ScriptEngine::setScriptContents(const QString& scriptContents, const QStrin · 3 unmodified lines
261273 return true;
262274 }
263275
276+// FIXME - remove the file/url scheme code here and let the script cache handle things
264277 void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) {
278+ qDebug() << "ScriptEngine::loadURL(" << scriptURL << ", " << reload << ");";
279+
265280 if (_isRunning) {
266281 return;
267282 }
@@ -298,7 +313,10 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) { · 30 unmodified lines
298313 } else {
299314 bool isPending;
300315 auto scriptCache = DependencyManager::get<ScriptCache>();
316+ qDebug() << "calling scriptCache->getScript(" << url << ", " << reload << ");";
301317 scriptCache->getScript(url, this, isPending, reload);
318+ qDebug() << " scriptCache->getScript(" << url << ", " << reload << ") ... isPending:" << isPending;
319+
302320 }
303321 }
304322 }
@@ -383,57 +401,97 @@ void ScriptEngine::init() { · 78 unmodified lines
383401 globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));
384402 }
385403
386−QScriptValue ScriptEngine::registerGlobalObject(const QString& name, QObject* object) {
404+void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) {
405+ if (QThread::currentThread() != thread()) {
406+ qDebug() << "*** WARNING *** ScriptEngine::registerGlobalObject() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name;
407+ QMetaObject::invokeMethod(this, "registerGlobalObject",
408+ Q_ARG(const QString&, name),
409+ Q_ARG(QObject*, object));
410+ return;
411+ }
412+ //qDebug() << "ScriptEngine::registerGlobalObject() called on thread [" << QThread::currentThread() << "] name:" << name;
413+
387414 if (object) {
388415 QScriptValue value = newQObject(object);
389416 globalObject().setProperty(name, value);
390− return value;
391417 }
392− return QScriptValue::NullValue;
393418 }
394419
395−void ScriptEngine::registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments) {
396− registerFunction(globalObject(), name, fun, numArguments);
420+void ScriptEngine::registerFunction(const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {
421+ if (QThread::currentThread() != thread()) {
422+ qDebug() << "*** WARNING *** ScriptEngine::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name;
423+ QMetaObject::invokeMethod(this, "registerFunction",
424+ Q_ARG(const QString&, name),
425+ Q_ARG(QScriptEngine::FunctionSignature, functionSignature),
426+ Q_ARG(int, numArguments));
427+ return;
428+ }
429+ //qDebug() << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] name:" << name;
430+
431+ QScriptValue scriptFun = newFunction(functionSignature, numArguments);
432+ globalObject().setProperty(name, scriptFun);
397433 }
398434
399−void ScriptEngine::registerFunction(QScriptValue parent, const QString& name, QScriptEngine::FunctionSignature fun, int numArguments) {
400− QScriptValue scriptFun = newFunction(fun, numArguments);
401− parent.setProperty(name, scriptFun);
435+void ScriptEngine::registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {
436+ if (QThread::currentThread() != thread()) {
437+ qDebug() << "*** WARNING *** ScriptEngine::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] parent:" << parent << "name:" << name;
438+ QMetaObject::invokeMethod(this, "registerFunction",
439+ Q_ARG(const QString&, name),
440+ Q_ARG(QScriptEngine::FunctionSignature, functionSignature),
441+ Q_ARG(int, numArguments));
442+ return;
443+ }
444+ //qDebug() << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] parent:" << parent << "name:" << name;
445+
446+ QScriptValue object = globalObject().property(parent);
447+ if (object.isValid()) {
448+ QScriptValue scriptFun = newFunction(functionSignature, numArguments);
449+ object.setProperty(name, scriptFun);
450+ }
402451 }
403452
404453 void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter,
405− QScriptEngine::FunctionSignature setter, QScriptValue object) {
454+ QScriptEngine::FunctionSignature setter, const QString& parent) {
455+ if (QThread::currentThread() != thread()) {
456+ qDebug() << "*** WARNING *** ScriptEngine::registerGetterSetter() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
457+ " name:" << name << "parent:" << parent;
458+ QMetaObject::invokeMethod(this, "registerGetterSetter",
459+ Q_ARG(const QString&, name),
460+ Q_ARG(QScriptEngine::FunctionSignature, getter),
461+ Q_ARG(QScriptEngine::FunctionSignature, setter),
462+ Q_ARG(const QString&, parent));
463+ return;
464+ }
465+ //qDebug() << "ScriptEngine::registerGetterSetter() called on thread [" << QThread::currentThread() << "] name:" << name << "parent:" << parent;
466+
406467 QScriptValue setterFunction = newFunction(setter, 1);
407468 QScriptValue getterFunction = newFunction(getter);
408469
409− if (!object.isNull()) {
410− object.setProperty(name, setterFunction, QScriptValue::PropertySetter);
411− object.setProperty(name, getterFunction, QScriptValue::PropertyGetter);
470+ if (!parent.isNull()) {
471+ QScriptValue object = globalObject().property(parent);
472+ if (object.isValid()) {
473+ object.setProperty(name, setterFunction, QScriptValue::PropertySetter);
474+ object.setProperty(name, getterFunction, QScriptValue::PropertyGetter);
475+ }
412476 } else {
413477 globalObject().setProperty(name, setterFunction, QScriptValue::PropertySetter);
414478 globalObject().setProperty(name, getterFunction, QScriptValue::PropertyGetter);
415479 }
416480 }
417481
418−// Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args
419−void ScriptEngine::generalHandler(const EntityItemID& entityID, const QString& eventName, std::function<QScriptValueList()> argGenerator) {
420− if (!_registeredHandlers.contains(entityID)) {
421− return;
422− }
423− const RegisteredEventHandlers& handlersOnEntity = _registeredHandlers[entityID];
424− if (!handlersOnEntity.contains(eventName)) {
425− return;
426− }
427− QScriptValueList handlersForEvent = handlersOnEntity[eventName];
428− if (!handlersForEvent.isEmpty()) {
429− QScriptValueList args = argGenerator();
430− for (int i = 0; i < handlersForEvent.count(); ++i) {
431− handlersForEvent[i].call(QScriptValue(), args);
432− }
433− }
434−}
435482 // Unregister the handlers for this eventName and entityID.
436483 void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) {
484+ if (QThread::currentThread() != thread()) {
485+ qDebug() << "*** WARNING *** ScriptEngine::removeEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
486+ "entityID:" << entityID << " eventName:" << eventName;
487+ QMetaObject::invokeMethod(this, "removeEventHandler",
488+ Q_ARG(const EntityItemID&, entityID),
489+ Q_ARG(const QString&, eventName),
490+ Q_ARG(QScriptValue, handler));
491+ return;
492+ }
493+ //qDebug() << "ScriptEngine::removeEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName;
494+
437495 if (!_registeredHandlers.contains(entityID)) {
438496 return;
439497 }
@@ -449,6 +507,17 @@ void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QStrin · 9 unmodified lines
449507 }
450508 // Register the handler.
451509 void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) {
510+ if (QThread::currentThread() != thread()) {
511+ qDebug() << "*** WARNING *** ScriptEngine::addEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
512+ "entityID:" << entityID << " eventName:" << eventName;
513+ QMetaObject::invokeMethod(this, "addEventHandler",
514+ Q_ARG(const EntityItemID&, entityID),
515+ Q_ARG(const QString&, eventName),
516+ Q_ARG(QScriptValue, handler));
517+ return;
518+ }
519+ //qDebug() << "ScriptEngine::addEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName;
520+
452521 if (_registeredHandlers.count() == 0) { // First time any per-entity handler has been added in this script...
453522 // Connect up ALL the handlers to the global entities object's signals.
454523 // (We could go signal by signal, or even handler by handler, but I don't think the efficiency is worth the complexity.)
@@ -503,34 +572,23 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& · 48 unmodified lines
503572 }
504573
505574
506−void ScriptEngine::evaluate() {
507− if (_stoppingAllScripts) {
508− return; // bail early
509− }
510−
511− if (!_isInitialized) {
512− init();
513− }
514−
515− QScriptValue result = evaluate(_scriptContents);
516−
517− // TODO: why do we check this twice? It seems like the call to clearExceptions() in the lower level evaluate call
518− // will cause this code to never actually run...
519− if (hasUncaughtException()) {
520− int line = uncaughtExceptionLineNumber();
521− qCDebug(scriptengine) << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << result.toString();
522− if (_wantSignals) {
523− emit errorMessage("Uncaught exception at (" + _fileNameString + ") line" + QString::number(line) + ":" + result.toString());
524− }
525− clearExceptions();
526− }
527−}
528−
529575 QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) {
530576 if (_stoppingAllScripts) {
531577 return QScriptValue(); // bail early
532578 }
533579
580+ if (QThread::currentThread() != thread()) {
581+ QScriptValue result;
582+ qDebug() << "*** WARNING *** ScriptEngine::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
583+ "program:" << program << " fileName:" << fileName << "lineNumber:" << lineNumber;
584+ QMetaObject::invokeMethod(this, "evaluate", Qt::BlockingQueuedConnection,
585+ Q_RETURN_ARG(QScriptValue, result),
586+ Q_ARG(const QString&, program),
587+ Q_ARG(const QString&, fileName),
588+ Q_ARG(int, lineNumber));
589+ return result;
590+ }
591+
534592 _evaluatesPending++;
535593 QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber);
536594 if (hasUncaughtException()) {
@@ -967,6 +1025,26 @@ void ScriptEngine::load(const QString& loadFile) { · 430 unmodified lines
9671025 }
9681026 }
9691027
970−void ScriptEngine::nodeKilled(SharedNodePointer node) {
971− _outgoingScriptAudioSequenceNumbers.remove(node->getUUID());
1028+// Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args
1029+void ScriptEngine::generalHandler(const EntityItemID& entityID, const QString& eventName, std::function<QScriptValueList()> argGenerator) {
1030+ if (QThread::currentThread() != thread()) {
1031+ qDebug() << "*** ERROR *** ScriptEngine::generalHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";
1032+ assert(false);
1033+ return;
1034+ }
1035+
1036+ if (!_registeredHandlers.contains(entityID)) {
1037+ return;
1038+ }
1039+ const RegisteredEventHandlers& handlersOnEntity = _registeredHandlers[entityID];
1040+ if (!handlersOnEntity.contains(eventName)) {
1041+ return;
1042+ }
1043+ QScriptValueList handlersForEvent = handlersOnEntity[eventName];
1044+ if (!handlersForEvent.isEmpty()) {
1045+ QScriptValueList args = argGenerator();
1046+ for (int i = 0; i < handlersForEvent.count(); ++i) {
1047+ handlersForEvent[i].call(QScriptValue(), args);
1048+ }
1049+ }
9721050 }