Showing the changes to libraries/script-engine/src/ScriptEngine.cpp (1 of 6 files). Show all
Unified · Split
| @@ -44,6 +44,8 @@ · 43 unmodified lines | |||
| 44 | 44 | ||
| 45 | #include "MIDIEvent.h" | 45 | #include "MIDIEvent.h" |
| 46 | 46 | ||
| 47 | Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature) | ||
| 48 | static int functionSignatureMetaID = qRegisterMetaType<QScriptEngine::FunctionSignature>(); | ||
| 47 | 49 | ||
| 48 | static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){ | 50 | static 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 | ||
| 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 | |||
| 125 | QSet<ScriptEngine*> ScriptEngine::_allKnownScriptEngines; | 147 | QSet<ScriptEngine*> ScriptEngine::_allKnownScriptEngines; |
| 126 | QMutex ScriptEngine::_allScriptsMutex; | 148 | QMutex ScriptEngine::_allScriptsMutex; |
| 127 | bool ScriptEngine::_stoppingAllScripts = false; | 149 | bool ScriptEngine::_stoppingAllScripts = false; |
| @@ -180,8 +202,6 @@ void ScriptEngine::stopAllScripts(QObject* application) { · 52 unmodified lines | |||
| 180 | 202 | ||
| 181 | 203 | ||
| 182 | void ScriptEngine::waitTillDoneRunning() { | 204 | void 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 it | 205 | // 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 | ||
| 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 | |||
| 255 | bool ScriptEngine::setScriptContents(const QString& scriptContents, const QString& fileNameString) { | 267 | bool 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 | ||
| 264 | void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) { | 277 | void 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 | ||
| 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 | |||
| 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 | ||
| 395 | void ScriptEngine::registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments) { | 420 | void 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 | ||
| 399 | void ScriptEngine::registerFunction(QScriptValue parent, const QString& name, QScriptEngine::FunctionSignature fun, int numArguments) { | 435 | void 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 | ||
| 404 | void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter, | 453 | 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 | |||
| 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 | ||
| 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 | } | ||
| 435 | // Unregister the handlers for this eventName and entityID. | 482 | // Unregister the handlers for this eventName and entityID. |
| 436 | void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) { | 483 | 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 | |||
| 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. |
| 451 | void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) { | 509 | 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 | |||
| 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 | ||
| 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 | |||
| 529 | QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) { | 575 | QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) { |
| 530 | if (_stoppingAllScripts) { | 576 | if (_stoppingAllScripts) { |
| 531 | return QScriptValue(); // bail early | 577 | 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 | ||
| 970 | void 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()); | 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 | } | ||
| 972 | } | 1050 | } |