History

Merge branch 'master' of https://github.com/highfidelity/hifi into expose-anim-vars

Howard Stearns committed 11 years ago

Commit d79d0bc2 parents 4b4907c d32477166 files · +728 −542Browse files

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

Unified · Split

libraries/script-engine/src/ScriptEngine.cpp+87−64
@@ -79,11 +79,41 @@ void avatarDataFromScriptValue(const QScriptValue &object, AvatarData* &out) { · 78 unmodified lines
7979 QScriptValue inputControllerToScriptValue(QScriptEngine *engine, AbstractInputController* const &in) {
8080 return engine->newQObject(in);
8181 }
82−
8382 void inputControllerFromScriptValue(const QScriptValue &object, AbstractInputController* &out) {
8483 out = qobject_cast<AbstractInputController*>(object.toQObject());
8584 }
8685
86+static bool hasCorrectSyntax(const QScriptProgram& program) {
87+ const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode());
88+ if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
89+ const auto error = syntaxCheck.errorMessage();
90+ const auto line = QString::number(syntaxCheck.errorLineNumber());
91+ const auto column = QString::number(syntaxCheck.errorColumnNumber());
92+ const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, program.fileName(), line, column);
93+ qCWarning(scriptengine) << qPrintable(message);
94+ return false;
95+ }
96+ return true;
97+}
98+
99+static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName) {
100+ if (engine.hasUncaughtException()) {
101+ const auto backtrace = engine.uncaughtExceptionBacktrace();
102+ const auto exception = engine.uncaughtException().toString();
103+ const auto line = QString::number(engine.uncaughtExceptionLineNumber());
104+ engine.clearExceptions();
105+
106+ auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line);
107+ if (!backtrace.empty()) {
108+ static const auto lineSeparator = "\n ";
109+ message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator));
110+ }
111+ qCWarning(scriptengine) << qPrintable(message);
112+ return true;
113+ }
114+ return false;
115+}
116+
87117 ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString,
88118 AbstractControllerScriptingInterface* controllerScriptingInterface, bool wantSignals) :
89119
@@ -95,9 +125,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam · 5 unmodified lines
95125 _wantSignals(wantSignals),
96126 _controllerScriptingInterface(controllerScriptingInterface),
97127 _fileNameString(fileNameString),
98− _quatLibrary(),
99− _vec3Library(),
100− _uuidLibrary(),
101128 _isUserLoaded(false),
102129 _isReloading(false),
103130 _arrayBufferClass(new ArrayBufferClass(this))
@@ -531,26 +558,33 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& · 427 unmodified lines
531558 // Connect up ALL the handlers to the global entities object's signals.
532559 // (We could go signal by signal, or even handler by handler, but I don't think the efficiency is worth the complexity.)
533560 auto entities = DependencyManager::get<EntityScriptingInterface>();
534− connect(entities.data(), &EntityScriptingInterface::deletingEntity, this,
535− [=](const EntityItemID& entityID) {
536− _registeredHandlers.remove(entityID);
537− });
538−
561+ connect(entities.data(), &EntityScriptingInterface::deletingEntity, this, [this](const EntityItemID& entityID) {
562+ _registeredHandlers.remove(entityID);
563+ });
564+
539565 // Two common cases of event handler, differing only in argument signature.
540− auto makeSingleEntityHandler = [=](const QString& eventName) -> std::function<void(const EntityItemID&)> {
541− return [=](const EntityItemID& entityItemID) -> void {
542− generalHandler(entityItemID, eventName, [=]() -> QScriptValueList {
543− return QScriptValueList() << entityItemID.toScriptValue(this);
544− });
566+ using SingleEntityHandler = std::function<void(const EntityItemID&)>;
567+ auto makeSingleEntityHandler = [this](QString eventName) -> SingleEntityHandler {
568+ return [this, eventName](const EntityItemID& entityItemID) {
569+ forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this) });
545570 };
546571 };
547− auto makeMouseHandler = [=](const QString& eventName) -> std::function<void(const EntityItemID&, const MouseEvent&)> {
548− return [=](const EntityItemID& entityItemID, const MouseEvent& event) -> void {
549− generalHandler(entityItemID, eventName, [=]() -> QScriptValueList {
550− return QScriptValueList() << entityItemID.toScriptValue(this) << event.toScriptValue(this);
551− });
572+
573+ using MouseHandler = std::function<void(const EntityItemID&, const MouseEvent&)>;
574+ auto makeMouseHandler = [this](QString eventName) -> MouseHandler {
575+ return [this, eventName](const EntityItemID& entityItemID, const MouseEvent& event) {
576+ forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) });
552577 };
553578 };
579+
580+ using CollisionHandler = std::function<void(const EntityItemID&, const EntityItemID&, const Collision&)>;
581+ auto makeCollisionHandler = [this](QString eventName) -> CollisionHandler {
582+ return [this, eventName](const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) {
583+ forwardHandlerCall(idA, eventName, { idA.toScriptValue(this), idB.toScriptValue(this),
584+ collisionToScriptValue(this, collision) });
585+ };
586+ };
587+
554588 connect(entities.data(), &EntityScriptingInterface::enterEntity, this, makeSingleEntityHandler("enterEntity"));
555589 connect(entities.data(), &EntityScriptingInterface::leaveEntity, this, makeSingleEntityHandler("leaveEntity"));
556590
@@ -566,12 +600,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& · 9 unmodified lines
566600 connect(entities.data(), &EntityScriptingInterface::hoverOverEntity, this, makeMouseHandler("hoverOverEntity"));
567601 connect(entities.data(), &EntityScriptingInterface::hoverLeaveEntity, this, makeMouseHandler("hoverLeaveEntity"));
568602
569− connect(entities.data(), &EntityScriptingInterface::collisionWithEntity, this,
570− [=](const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) {
571− generalHandler(idA, "collisionWithEntity", [=]() {
572− return QScriptValueList () << idA.toScriptValue(this) << idB.toScriptValue(this) << collisionToScriptValue(this, collision);
573− });
574− });
603+ connect(entities.data(), &EntityScriptingInterface::collisionWithEntity, this, makeCollisionHandler("collisionWithEntity"));
575604 }
576605 if (!_registeredHandlers.contains(entityID)) {
577606 _registeredHandlers[entityID] = RegisteredEventHandlers();
@@ -581,7 +610,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& · 3 unmodified lines
581610 }
582611
583612
584−QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) {
613+QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fileName, int lineNumber) {
585614 if (_stoppingAllScripts) {
586615 return QScriptValue(); // bail early
587616 }
@@ -590,27 +619,30 @@ QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileN · 2 unmodified lines
590619 QScriptValue result;
591620 #ifdef THREAD_DEBUGGING
592621 qDebug() << "*** WARNING *** ScriptEngine::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
593− "program:" << program << " fileName:" << fileName << "lineNumber:" << lineNumber;
622+ "sourceCode:" << sourceCode << " fileName:" << fileName << "lineNumber:" << lineNumber;
594623 #endif
595624 QMetaObject::invokeMethod(this, "evaluate", Qt::BlockingQueuedConnection,
596625 Q_RETURN_ARG(QScriptValue, result),
597− Q_ARG(const QString&, program),
626+ Q_ARG(const QString&, sourceCode),
598627 Q_ARG(const QString&, fileName),
599628 Q_ARG(int, lineNumber));
600629 return result;
601630 }
602−
603− _evaluatesPending++;
604− QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber);
605− if (hasUncaughtException()) {
606− int line = uncaughtExceptionLineNumber();
607− qCDebug(scriptengine) << "Uncaught exception at (" << _fileNameString << " : " << fileName << ") line" << line << ": " << result.toString();
631+
632+ // Check syntax
633+ const QScriptProgram program(sourceCode, fileName, lineNumber);
634+ if (!hasCorrectSyntax(program)) {
635+ return QScriptValue();
608636 }
609− _evaluatesPending--;
637+
638+ ++_evaluatesPending;
639+ const auto result = QScriptEngine::evaluate(program);
640+ --_evaluatesPending;
641+
642+ const auto hadUncaughtException = hadUncaughtExceptions(*this, program.fileName());
610643 if (_wantSignals) {
611− emit evaluationFinished(result, hasUncaughtException());
644+ emit evaluationFinished(result, hadUncaughtException);
612645 }
613− clearExceptions();
614646 return result;
615647 }
616648
@@ -628,7 +660,7 @@ void ScriptEngine::run() { · 11 unmodified lines
628660 emit runningStateChanged();
629661 }
630662
631− QScriptValue result = evaluate(_scriptContents);
663+ QScriptValue result = evaluate(_scriptContents, _fileNameString);
632664
633665 QElapsedTimer startTime;
634666 startTime.start();
@@ -669,15 +701,6 @@ void ScriptEngine::run() { · 34 unmodified lines
669701 qint64 now = usecTimestampNow();
670702 float deltaTime = (float) (now - lastUpdate) / (float) USECS_PER_SECOND;
671703
672− if (hasUncaughtException()) {
673− int line = uncaughtExceptionLineNumber();
674− qCDebug(scriptengine) << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << uncaughtException().toString();
675− if (_wantSignals) {
676− emit errorMessage("Uncaught exception at (" + _fileNameString + ") line" + QString::number(line) + ":" + uncaughtException().toString());
677− }
678− clearExceptions();
679− }
680−
681704 if (!_isFinished) {
682705 if (_wantSignals) {
683706 emit update(deltaTime);
@@ -685,6 +708,8 @@ void ScriptEngine::run() { · 1 unmodified line
685708 }
686709 lastUpdate = now;
687710
711+ // Debug and clear exceptions
712+ hadUncaughtExceptions(*this, _fileNameString);
688713 }
689714
690715 stopAllTimers(); // make sure all our timers are stopped if the script is ending
@@ -943,9 +968,9 @@ void ScriptEngine::load(const QString& loadFile) { · 252 unmodified lines
943968 }
944969
945970 // Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args
946−void ScriptEngine::generalHandler(const EntityItemID& entityID, const QString& eventName, std::function<QScriptValueList()> argGenerator) {
971+void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs) {
947972 if (QThread::currentThread() != thread()) {
948− qDebug() << "*** ERROR *** ScriptEngine::generalHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";
973+ qDebug() << "*** ERROR *** ScriptEngine::forwardHandlerCall() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";
949974 assert(false);
950975 return ;
951976 }
@@ -958,9 +983,8 @@ void ScriptEngine::generalHandler(const EntityItemID& entityID, const QString& e · 6 unmodified lines
958983 }
959984 QScriptValueList handlersForEvent = handlersOnEntity[eventName];
960985 if (!handlersForEvent.isEmpty()) {
961− QScriptValueList args = argGenerator();
962986 for (int i = 0; i < handlersForEvent.count(); ++i) {
963− handlersForEvent[i].call(QScriptValue(), args);
987+ handlersForEvent[i].call(QScriptValue(), eventHanderArgs);
964988 }
965989 }
966990 }
@@ -1024,14 +1048,10 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 57 unmodified lines
10241048
10251049 auto scriptCache = DependencyManager::get<ScriptCache>();
10261050 bool isFileUrl = isURL && scriptOrURL.startsWith("file://");
1051+ auto fileName = QString("(EntityID:%1, %2)").arg(entityID.toString(), isURL ? scriptOrURL : "EmbededEntityScript");
10271052
1028− // first check the syntax of the script contents
1029− QScriptSyntaxCheckResult syntaxCheck = QScriptEngine::checkSyntax(contents);
1030− if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
1031− qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID;
1032− qCDebug(scriptengine) << " " << syntaxCheck.errorMessage() << ":"
1033− << syntaxCheck.errorLineNumber() << syntaxCheck.errorColumnNumber();
1034− qCDebug(scriptengine) << " SCRIPT:" << scriptOrURL;
1053+ QScriptProgram program(contents, fileName);
1054+ if (!hasCorrectSyntax(program)) {
10351055 if (!isFileUrl) {
10361056 scriptCache->addScriptToBadScriptList(scriptOrURL);
10371057 }
@@ -1043,12 +1063,15 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 5 unmodified lines
10431063 }
10441064
10451065 QScriptEngine sandbox;
1046− QScriptValue testConstructor = sandbox.evaluate(contents);
1047−
1066+ QScriptValue testConstructor = sandbox.evaluate(program);
1067+ if (hadUncaughtExceptions(sandbox, program.fileName())) {
1068+ return;
1069+ }
1070+
10481071 if (!testConstructor.isFunction()) {
1049− qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID;
1050− qCDebug(scriptengine) << " NOT CONSTRUCTOR";
1051− qCDebug(scriptengine) << " SCRIPT:" << scriptOrURL;
1072+ qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID << "\n"
1073+ " NOT CONSTRUCTOR\n"
1074+ " SCRIPT:" << scriptOrURL;
10521075 if (!isFileUrl) {
10531076 scriptCache->addScriptToBadScriptList(scriptOrURL);
10541077 }
@@ -1061,7 +1084,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 6 unmodified lines
10611084 lastModified = (quint64)QFileInfo(file).lastModified().toMSecsSinceEpoch();
10621085 }
10631086
1064− QScriptValue entityScriptConstructor = evaluate(contents);
1087+ QScriptValue entityScriptConstructor = evaluate(contents, fileName);
10651088 QScriptValue entityScriptObject = entityScriptConstructor.construct();
10661089 EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified };
10671090 _entityScripts[entityID] = newDetails;