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
44 44
45#include "MIDIEvent.h"45#include "MIDIEvent.h"
46 46
47Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature)
48static int functionSignatureMetaID = qRegisterMetaType<QScriptEngine::FunctionSignature>();
47 49
48static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){50static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){
49 QString message = "";51 QString message = "";
@@ -97,7 +99,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam · 47 unmodified lines
97 _controllerScriptingInterface(controllerScriptingInterface),99 _controllerScriptingInterface(controllerScriptingInterface),
98 _wantSignals(wantSignals),100 _wantSignals(wantSignals),
99 _avatarData(NULL),101 _avatarData(NULL),
100 _scriptName(),
101 _fileNameString(fileNameString),102 _fileNameString(fileNameString),
102 _quatLibrary(),103 _quatLibrary(),
103 _vec3Library(),104 _vec3Library(),
@@ -122,6 +123,27 @@ ScriptEngine::~ScriptEngine() { · 18 unmodified lines
122 }123 }
123}124}
124 125
126void 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
125QSet<ScriptEngine*> ScriptEngine::_allKnownScriptEngines;147QSet<ScriptEngine*> ScriptEngine::_allKnownScriptEngines;
126QMutex ScriptEngine::_allScriptsMutex;148QMutex ScriptEngine::_allScriptsMutex;
127bool ScriptEngine::_stoppingAllScripts = false;149bool ScriptEngine::_stoppingAllScripts = false;
@@ -180,8 +202,6 @@ void ScriptEngine::stopAllScripts(QObject* application) { · 52 unmodified lines
180 202
181 203
182void ScriptEngine::waitTillDoneRunning() {204void ScriptEngine::waitTillDoneRunning() {
183 QString scriptName = getFilename();
184
185 // If the script never started running or finished running before we got here, we don't need to wait for it205 // If the script never started running or finished running before we got here, we don't need to wait for it
186 if (_isRunning) {206 if (_isRunning) {
187 207
@@ -244,14 +264,6 @@ void ScriptEngine::setAvatarData(AvatarData* avatarData, const QString& objectNa · 56 unmodified lines
244 registerGlobalObject(objectName, _avatarData);264 registerGlobalObject(objectName, _avatarData);
245}265}
246 266
247void 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
255bool ScriptEngine::setScriptContents(const QString& scriptContents, const QString& fileNameString) {267bool ScriptEngine::setScriptContents(const QString& scriptContents, const QString& fileNameString) {
256 if (_isRunning) {268 if (_isRunning) {
257 return false;269 return false;
@@ -261,7 +273,10 @@ bool ScriptEngine::setScriptContents(const QString& scriptContents, const QStrin · 3 unmodified lines
261 return true;273 return true;
262}274}
263 275
276// FIXME - remove the file/url scheme code here and let the script cache handle things
264void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) {277void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) {
278 qDebug() << "ScriptEngine::loadURL(" << scriptURL << ", " << reload << ");";
279
265 if (_isRunning) {280 if (_isRunning) {
266 return;281 return;
267 }282 }
@@ -298,7 +313,10 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) { · 30 unmodified lines
298 } else {313 } else {
299 bool isPending;314 bool isPending;
300 auto scriptCache = DependencyManager::get<ScriptCache>();315 auto scriptCache = DependencyManager::get<ScriptCache>();
316 qDebug() << "calling scriptCache->getScript(" << url << ", " << reload << ");";
301 scriptCache->getScript(url, this, isPending, reload);317 scriptCache->getScript(url, this, isPending, reload);
318 qDebug() << " scriptCache->getScript(" << url << ", " << reload << ") ... isPending:" << isPending;
319
302 }320 }
303 }321 }
304}322}
@@ -383,57 +401,97 @@ void ScriptEngine::init() { · 78 unmodified lines
383 globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));401 globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));
384}402}
385 403
386QScriptValue ScriptEngine::registerGlobalObject(const QString& name, QObject* object) {404void 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
387 if (object) {414 if (object) {
388 QScriptValue value = newQObject(object);415 QScriptValue value = newQObject(object);
389 globalObject().setProperty(name, value);416 globalObject().setProperty(name, value);
390 return value;
391 }417 }
392 return QScriptValue::NullValue;
393}418}
394 419
395void ScriptEngine::registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments) {420void ScriptEngine::registerFunction(const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {
396 registerFunction(globalObject(), name, fun, 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);
397}433}
398 434
399void ScriptEngine::registerFunction(QScriptValue parent, const QString& name, QScriptEngine::FunctionSignature fun, int numArguments) {435void ScriptEngine::registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {
400 QScriptValue scriptFun = newFunction(fun, numArguments);436 if (QThread::currentThread() != thread()) {
401 parent.setProperty(name, scriptFun);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 }
402}451}
403 452
404void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter,453void 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
406 QScriptValue setterFunction = newFunction(setter, 1);467 QScriptValue setterFunction = newFunction(setter, 1);
407 QScriptValue getterFunction = newFunction(getter);468 QScriptValue getterFunction = newFunction(getter);
408 469
409 if (!object.isNull()) {470 if (!parent.isNull()) {
410 object.setProperty(name, setterFunction, QScriptValue::PropertySetter);471 QScriptValue object = globalObject().property(parent);
411 object.setProperty(name, getterFunction, QScriptValue::PropertyGetter);472 if (object.isValid()) {
473 object.setProperty(name, setterFunction, QScriptValue::PropertySetter);
474 object.setProperty(name, getterFunction, QScriptValue::PropertyGetter);
475 }
412 } else {476 } else {
413 globalObject().setProperty(name, setterFunction, QScriptValue::PropertySetter);477 globalObject().setProperty(name, setterFunction, QScriptValue::PropertySetter);
414 globalObject().setProperty(name, getterFunction, QScriptValue::PropertyGetter);478 globalObject().setProperty(name, getterFunction, QScriptValue::PropertyGetter);
415 }479 }
416}480}
417 481
418// Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args
419void 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}
435// Unregister the handlers for this eventName and entityID.482// Unregister the handlers for this eventName and entityID.
436void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) {483void 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
437 if (!_registeredHandlers.contains(entityID)) {495 if (!_registeredHandlers.contains(entityID)) {
438 return;496 return;
439 }497 }
@@ -449,6 +507,17 @@ void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QStrin · 9 unmodified lines
449}507}
450// Register the handler.508// Register the handler.
451void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) {509void 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
452 if (_registeredHandlers.count() == 0) { // First time any per-entity handler has been added in this script...521 if (_registeredHandlers.count() == 0) { // First time any per-entity handler has been added in this script...
453 // Connect up ALL the handlers to the global entities object's signals.522 // Connect up ALL the handlers to the global entities object's signals.
454 // (We could go signal by signal, or even handler by handler, but I don't think the efficiency is worth the complexity.)523 // (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
503}572}
504 573
505 574
506void 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
529QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) {575QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) {
530 if (_stoppingAllScripts) {576 if (_stoppingAllScripts) {
531 return QScriptValue(); // bail early577 return QScriptValue(); // bail early
532 }578 }
533 579
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
534 _evaluatesPending++;592 _evaluatesPending++;
535 QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber);593 QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber);
536 if (hasUncaughtException()) {594 if (hasUncaughtException()) {
@@ -967,6 +1025,26 @@ void ScriptEngine::load(const QString& loadFile) { · 430 unmodified lines
967 }1025 }
968}1026}
969 1027
970void ScriptEngine::nodeKilled(SharedNodePointer node) {1028// Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args
971 _outgoingScriptAudioSequenceNumbers.remove(node->getUUID());1029void 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 }
972}1050}