History

Merge remote-tracking branch 'upstream/master' into HEAD

Brad Davis committed 11 years ago

Commit 3c1a0932 parents 3be06a5 731fab653 files · +600 −413Browse files

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

Unified · Split

libraries/script-engine/src/ScriptEngine.cpp+90−64
@@ -86,8 +86,40 @@ void inputControllerFromScriptValue(const QScriptValue &object, controller::Inpu · 85 unmodified lines
86 out = qobject_cast<controller::InputController*>(object.toQObject());86 out = qobject_cast<controller::InputController*>(object.toQObject());
87}87}
88 88
89ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, bool wantSignals) :
90 89
90
91static bool hasCorrectSyntax(const QScriptProgram& program) {
92 const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode());
93 if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
94 const auto error = syntaxCheck.errorMessage();
95 const auto line = QString::number(syntaxCheck.errorLineNumber());
96 const auto column = QString::number(syntaxCheck.errorColumnNumber());
97 const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, program.fileName(), line, column);
98 qCWarning(scriptengine) << qPrintable(message);
99 return false;
100 }
101 return true;
102}
103
104static bool hadUncauchtExceptions(QScriptEngine& engine, const QString& fileName) {
105 if (engine.hasUncaughtException()) {
106 const auto backtrace = engine.uncaughtExceptionBacktrace();
107 const auto exception = engine.uncaughtException().toString();
108 const auto line = QString::number(engine.uncaughtExceptionLineNumber());
109 engine.clearExceptions();
110
111 auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line);
112 if (!backtrace.empty()) {
113 static const auto lineSeparator = "\n ";
114 message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator));
115 }
116 qCWarning(scriptengine) << qPrintable(message);
117 return true;
118 }
119 return false;
120}
121
122ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, bool wantSignals) :
91 _scriptContents(scriptContents),123 _scriptContents(scriptContents),
92 _isFinished(false),124 _isFinished(false),
93 _isRunning(false),125 _isRunning(false),
@@ -95,9 +127,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam · 1 unmodified line
95 _timerFunctionMap(),127 _timerFunctionMap(),
96 _wantSignals(wantSignals),128 _wantSignals(wantSignals),
97 _fileNameString(fileNameString),129 _fileNameString(fileNameString),
98 _quatLibrary(),
99 _vec3Library(),
100 _uuidLibrary(),
101 _isUserLoaded(false),130 _isUserLoaded(false),
102 _isReloading(false),131 _isReloading(false),
103 _arrayBufferClass(new ArrayBufferClass(this))132 _arrayBufferClass(new ArrayBufferClass(this))
@@ -505,26 +534,33 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& · 401 unmodified lines
505 // Connect up ALL the handlers to the global entities object's signals.534 // Connect up ALL the handlers to the global entities object's signals.
506 // (We could go signal by signal, or even handler by handler, but I don't think the efficiency is worth the complexity.)535 // (We could go signal by signal, or even handler by handler, but I don't think the efficiency is worth the complexity.)
507 auto entities = DependencyManager::get<EntityScriptingInterface>();536 auto entities = DependencyManager::get<EntityScriptingInterface>();
508 connect(entities.data(), &EntityScriptingInterface::deletingEntity, this,537 connect(entities.data(), &EntityScriptingInterface::deletingEntity, this, [this](const EntityItemID& entityID) {
509 [=](const EntityItemID& entityID) {538 _registeredHandlers.remove(entityID);
510 _registeredHandlers.remove(entityID);539 });
511 });540
512
513 // Two common cases of event handler, differing only in argument signature.541 // Two common cases of event handler, differing only in argument signature.
514 auto makeSingleEntityHandler = [=](const QString& eventName) -> std::function<void(const EntityItemID&)> {542 using SingleEntityHandler = std::function<void(const EntityItemID&)>;
515 return [=](const EntityItemID& entityItemID) -> void {543 auto makeSingleEntityHandler = [this](QString eventName) -> SingleEntityHandler {
516 generalHandler(entityItemID, eventName, [=]() -> QScriptValueList {544 return [this, eventName](const EntityItemID& entityItemID) {
517 return QScriptValueList() << entityItemID.toScriptValue(this);545 forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this) });
518 });
519 };546 };
520 };547 };
521 auto makeMouseHandler = [=](const QString& eventName) -> std::function<void(const EntityItemID&, const MouseEvent&)> {548
522 return [=](const EntityItemID& entityItemID, const MouseEvent& event) -> void {549 using MouseHandler = std::function<void(const EntityItemID&, const MouseEvent&)>;
523 generalHandler(entityItemID, eventName, [=]() -> QScriptValueList {550 auto makeMouseHandler = [this](QString eventName) -> MouseHandler {
524 return QScriptValueList() << entityItemID.toScriptValue(this) << event.toScriptValue(this);551 return [this, eventName](const EntityItemID& entityItemID, const MouseEvent& event) {
525 });552 forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) });
526 };553 };
527 };554 };
555
556 using CollisionHandler = std::function<void(const EntityItemID&, const EntityItemID&, const Collision&)>;
557 auto makeCollisionHandler = [this](QString eventName) -> CollisionHandler {
558 return [this, eventName](const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) {
559 forwardHandlerCall(idA, eventName, { idA.toScriptValue(this), idB.toScriptValue(this),
560 collisionToScriptValue(this, collision) });
561 };
562 };
563
528 connect(entities.data(), &EntityScriptingInterface::enterEntity, this, makeSingleEntityHandler("enterEntity"));564 connect(entities.data(), &EntityScriptingInterface::enterEntity, this, makeSingleEntityHandler("enterEntity"));
529 connect(entities.data(), &EntityScriptingInterface::leaveEntity, this, makeSingleEntityHandler("leaveEntity"));565 connect(entities.data(), &EntityScriptingInterface::leaveEntity, this, makeSingleEntityHandler("leaveEntity"));
530 566
@@ -540,12 +576,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& · 9 unmodified lines
540 connect(entities.data(), &EntityScriptingInterface::hoverOverEntity, this, makeMouseHandler("hoverOverEntity"));576 connect(entities.data(), &EntityScriptingInterface::hoverOverEntity, this, makeMouseHandler("hoverOverEntity"));
541 connect(entities.data(), &EntityScriptingInterface::hoverLeaveEntity, this, makeMouseHandler("hoverLeaveEntity"));577 connect(entities.data(), &EntityScriptingInterface::hoverLeaveEntity, this, makeMouseHandler("hoverLeaveEntity"));
542 578
543 connect(entities.data(), &EntityScriptingInterface::collisionWithEntity, this,579 connect(entities.data(), &EntityScriptingInterface::collisionWithEntity, this, makeCollisionHandler("collisionWithEntity"));
544 [=](const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) {
545 generalHandler(idA, "collisionWithEntity", [=]() {
546 return QScriptValueList () << idA.toScriptValue(this) << idB.toScriptValue(this) << collisionToScriptValue(this, collision);
547 });
548 });
549 }580 }
550 if (!_registeredHandlers.contains(entityID)) {581 if (!_registeredHandlers.contains(entityID)) {
551 _registeredHandlers[entityID] = RegisteredEventHandlers();582 _registeredHandlers[entityID] = RegisteredEventHandlers();
@@ -555,7 +586,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& · 3 unmodified lines
555}586}
556 587
557 588
558QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) {589QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fileName, int lineNumber) {
559 if (_stoppingAllScripts) {590 if (_stoppingAllScripts) {
560 return QScriptValue(); // bail early591 return QScriptValue(); // bail early
561 }592 }
@@ -564,27 +595,30 @@ QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileN · 2 unmodified lines
564 QScriptValue result;595 QScriptValue result;
565 #ifdef THREAD_DEBUGGING596 #ifdef THREAD_DEBUGGING
566 qDebug() << "*** WARNING *** ScriptEngine::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "597 qDebug() << "*** WARNING *** ScriptEngine::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
567 "program:" << program << " fileName:" << fileName << "lineNumber:" << lineNumber;598 "sourceCode:" << sourceCode << " fileName:" << fileName << "lineNumber:" << lineNumber;
568 #endif599 #endif
569 QMetaObject::invokeMethod(this, "evaluate", Qt::BlockingQueuedConnection,600 QMetaObject::invokeMethod(this, "evaluate", Qt::BlockingQueuedConnection,
570 Q_RETURN_ARG(QScriptValue, result),601 Q_RETURN_ARG(QScriptValue, result),
571 Q_ARG(const QString&, program),602 Q_ARG(const QString&, sourceCode),
572 Q_ARG(const QString&, fileName),603 Q_ARG(const QString&, fileName),
573 Q_ARG(int, lineNumber));604 Q_ARG(int, lineNumber));
574 return result;605 return result;
575 }606 }
576 607
577 _evaluatesPending++;608 // Check syntax
578 QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber);609 const QScriptProgram program(sourceCode, fileName, lineNumber);
579 if (hasUncaughtException()) {610 if (!hasCorrectSyntax(program)) {
580 int line = uncaughtExceptionLineNumber();611 return QScriptValue();
581 qCDebug(scriptengine) << "Uncaught exception at (" << _fileNameString << " : " << fileName << ") line" << line << ": " << result.toString();
582 }612 }
583 _evaluatesPending--;613
614 ++_evaluatesPending;
615 const auto result = QScriptEngine::evaluate(program);
616 --_evaluatesPending;
617
618 const auto hadUncaughtException = hadUncauchtExceptions(*this, program.fileName());
584 if (_wantSignals) {619 if (_wantSignals) {
585 emit evaluationFinished(result, hasUncaughtException());620 emit evaluationFinished(result, hadUncaughtException);
586 }621 }
587 clearExceptions();
588 return result;622 return result;
589}623}
590 624
@@ -602,7 +636,7 @@ void ScriptEngine::run() { · 11 unmodified lines
602 emit runningStateChanged();636 emit runningStateChanged();
603 }637 }
604 638
605 QScriptValue result = evaluate(_scriptContents);639 QScriptValue result = evaluate(_scriptContents, _fileNameString);
606 640
607 QElapsedTimer startTime;641 QElapsedTimer startTime;
608 startTime.start();642 startTime.start();
@@ -643,15 +677,6 @@ void ScriptEngine::run() { · 34 unmodified lines
643 qint64 now = usecTimestampNow();677 qint64 now = usecTimestampNow();
644 float deltaTime = (float) (now - lastUpdate) / (float) USECS_PER_SECOND;678 float deltaTime = (float) (now - lastUpdate) / (float) USECS_PER_SECOND;
645 679
646 if (hasUncaughtException()) {
647 int line = uncaughtExceptionLineNumber();
648 qCDebug(scriptengine) << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << uncaughtException().toString();
649 if (_wantSignals) {
650 emit errorMessage("Uncaught exception at (" + _fileNameString + ") line" + QString::number(line) + ":" + uncaughtException().toString());
651 }
652 clearExceptions();
653 }
654
655 if (!_isFinished) {680 if (!_isFinished) {
656 if (_wantSignals) {681 if (_wantSignals) {
657 emit update(deltaTime);682 emit update(deltaTime);
@@ -659,6 +684,9 @@ void ScriptEngine::run() { · 1 unmodified line
659 }684 }
660 lastUpdate = now;685 lastUpdate = now;
661 686
687 if (hadUncauchtExceptions(*this, _fileNameString)) {
688 stop();
689 }
662 }690 }
663 691
664 stopAllTimers(); // make sure all our timers are stopped if the script is ending692 stopAllTimers(); // make sure all our timers are stopped if the script is ending
@@ -896,9 +924,9 @@ void ScriptEngine::load(const QString& loadFile) { · 231 unmodified lines
896}924}
897 925
898// Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args926// Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args
899void ScriptEngine::generalHandler(const EntityItemID& entityID, const QString& eventName, std::function<QScriptValueList()> argGenerator) {927void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs) {
900 if (QThread::currentThread() != thread()) {928 if (QThread::currentThread() != thread()) {
901 qDebug() << "*** ERROR *** ScriptEngine::generalHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";929 qDebug() << "*** ERROR *** ScriptEngine::forwardHandlerCall() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";
902 assert(false);930 assert(false);
903 return;931 return;
904 }932 }
@@ -912,9 +940,8 @@ void ScriptEngine::generalHandler(const EntityItemID& entityID, const QString& e · 7 unmodified lines
912 }940 }
913 QScriptValueList handlersForEvent = handlersOnEntity[eventName];941 QScriptValueList handlersForEvent = handlersOnEntity[eventName];
914 if (!handlersForEvent.isEmpty()) {942 if (!handlersForEvent.isEmpty()) {
915 QScriptValueList args = argGenerator();
916 for (int i = 0; i < handlersForEvent.count(); ++i) {943 for (int i = 0; i < handlersForEvent.count(); ++i) {
917 handlersForEvent[i].call(QScriptValue(), args);944 handlersForEvent[i].call(QScriptValue(), eventHanderArgs);
918 }945 }
919 }946 }
920}947}
@@ -978,14 +1005,10 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 57 unmodified lines
978 1005
979 auto scriptCache = DependencyManager::get<ScriptCache>();1006 auto scriptCache = DependencyManager::get<ScriptCache>();
980 bool isFileUrl = isURL && scriptOrURL.startsWith("file://");1007 bool isFileUrl = isURL && scriptOrURL.startsWith("file://");
1008 auto fileName = QString("(EntityID:%1, %2)").arg(entityID.toString(), isURL ? scriptOrURL : "EmbededEntityScript");
981 1009
982 // first check the syntax of the script contents1010 QScriptProgram program(contents, fileName);
983 QScriptSyntaxCheckResult syntaxCheck = QScriptEngine::checkSyntax(contents);1011 if (!hasCorrectSyntax(program)) {
984 if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
985 qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID;
986 qCDebug(scriptengine) << " " << syntaxCheck.errorMessage() << ":"
987 << syntaxCheck.errorLineNumber() << syntaxCheck.errorColumnNumber();
988 qCDebug(scriptengine) << " SCRIPT:" << scriptOrURL;
989 if (!isFileUrl) {1012 if (!isFileUrl) {
990 scriptCache->addScriptToBadScriptList(scriptOrURL);1013 scriptCache->addScriptToBadScriptList(scriptOrURL);
991 }1014 }
@@ -997,12 +1020,15 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 5 unmodified lines
997 }1020 }
998 1021
999 QScriptEngine sandbox;1022 QScriptEngine sandbox;
1000 QScriptValue testConstructor = sandbox.evaluate(contents);1023 QScriptValue testConstructor = sandbox.evaluate(program);
1001 1024 if (hadUncauchtExceptions(sandbox, program.fileName())) {
1025 return;
1026 }
1027
1002 if (!testConstructor.isFunction()) {1028 if (!testConstructor.isFunction()) {
1003 qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID;1029 qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID << "\n"
1004 qCDebug(scriptengine) << " NOT CONSTRUCTOR";1030 " NOT CONSTRUCTOR\n"
1005 qCDebug(scriptengine) << " SCRIPT:" << scriptOrURL;1031 " SCRIPT:" << scriptOrURL;
1006 if (!isFileUrl) {1032 if (!isFileUrl) {
1007 scriptCache->addScriptToBadScriptList(scriptOrURL);1033 scriptCache->addScriptToBadScriptList(scriptOrURL);
1008 }1034 }
@@ -1015,7 +1041,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 6 unmodified lines
1015 lastModified = (quint64)QFileInfo(file).lastModified().toMSecsSinceEpoch();1041 lastModified = (quint64)QFileInfo(file).lastModified().toMSecsSinceEpoch();
1016 }1042 }
1017 1043
1018 QScriptValue entityScriptConstructor = evaluate(contents);1044 QScriptValue entityScriptConstructor = evaluate(contents, fileName);
1019 QScriptValue entityScriptObject = entityScriptConstructor.construct();1045 QScriptValue entityScriptObject = entityScriptConstructor.construct();
1020 EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified };1046 EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified };
1021 _entityScripts[entityID] = newDetails;1047 _entityScripts[entityID] = newDetails;