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
8686 out = qobject_cast<controller::InputController*>(object.toQObject());
8787 }
8888
89−ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, bool wantSignals) :
9089
90+
91+static 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+
104+static 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+
122+ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, bool wantSignals) :
91123 _scriptContents(scriptContents),
92124 _isFinished(false),
93125 _isRunning(false),
@@ -95,9 +127,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam · 1 unmodified line
95127 _timerFunctionMap(),
96128 _wantSignals(wantSignals),
97129 _fileNameString(fileNameString),
98− _quatLibrary(),
99− _vec3Library(),
100− _uuidLibrary(),
101130 _isUserLoaded(false),
102131 _isReloading(false),
103132 _arrayBufferClass(new ArrayBufferClass(this))
@@ -505,26 +534,33 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& · 401 unmodified lines
505534 // Connect up ALL the handlers to the global entities object's signals.
506535 // (We could go signal by signal, or even handler by handler, but I don't think the efficiency is worth the complexity.)
507536 auto entities = DependencyManager::get<EntityScriptingInterface>();
508− connect(entities.data(), &EntityScriptingInterface::deletingEntity, this,
509− [=](const EntityItemID& entityID) {
510− _registeredHandlers.remove(entityID);
511− });
512−
537+ connect(entities.data(), &EntityScriptingInterface::deletingEntity, this, [this](const EntityItemID& entityID) {
538+ _registeredHandlers.remove(entityID);
539+ });
540+
513541 // Two common cases of event handler, differing only in argument signature.
514− auto makeSingleEntityHandler = [=](const QString& eventName) -> std::function<void(const EntityItemID&)> {
515− return [=](const EntityItemID& entityItemID) -> void {
516− generalHandler(entityItemID, eventName, [=]() -> QScriptValueList {
517− return QScriptValueList() << entityItemID.toScriptValue(this);
518− });
542+ using SingleEntityHandler = std::function<void(const EntityItemID&)>;
543+ auto makeSingleEntityHandler = [this](QString eventName) -> SingleEntityHandler {
544+ return [this, eventName](const EntityItemID& entityItemID) {
545+ forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this) });
519546 };
520547 };
521− auto makeMouseHandler = [=](const QString& eventName) -> std::function<void(const EntityItemID&, const MouseEvent&)> {
522− return [=](const EntityItemID& entityItemID, const MouseEvent& event) -> void {
523− generalHandler(entityItemID, eventName, [=]() -> QScriptValueList {
524− return QScriptValueList() << entityItemID.toScriptValue(this) << event.toScriptValue(this);
525− });
548+
549+ using MouseHandler = std::function<void(const EntityItemID&, const MouseEvent&)>;
550+ auto makeMouseHandler = [this](QString eventName) -> MouseHandler {
551+ return [this, eventName](const EntityItemID& entityItemID, const MouseEvent& event) {
552+ forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) });
526553 };
527554 };
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+
528564 connect(entities.data(), &EntityScriptingInterface::enterEntity, this, makeSingleEntityHandler("enterEntity"));
529565 connect(entities.data(), &EntityScriptingInterface::leaveEntity, this, makeSingleEntityHandler("leaveEntity"));
530566
@@ -540,12 +576,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& · 9 unmodified lines
540576 connect(entities.data(), &EntityScriptingInterface::hoverOverEntity, this, makeMouseHandler("hoverOverEntity"));
541577 connect(entities.data(), &EntityScriptingInterface::hoverLeaveEntity, this, makeMouseHandler("hoverLeaveEntity"));
542578
543− connect(entities.data(), &EntityScriptingInterface::collisionWithEntity, this,
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− });
579+ connect(entities.data(), &EntityScriptingInterface::collisionWithEntity, this, makeCollisionHandler("collisionWithEntity"));
549580 }
550581 if (!_registeredHandlers.contains(entityID)) {
551582 _registeredHandlers[entityID] = RegisteredEventHandlers();
@@ -555,7 +586,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& · 3 unmodified lines
555586 }
556587
557588
558−QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) {
589+QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fileName, int lineNumber) {
559590 if (_stoppingAllScripts) {
560591 return QScriptValue(); // bail early
561592 }
@@ -564,27 +595,30 @@ QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileN · 2 unmodified lines
564595 QScriptValue result;
565596 #ifdef THREAD_DEBUGGING
566597 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;
568599 #endif
569600 QMetaObject::invokeMethod(this, "evaluate", Qt::BlockingQueuedConnection,
570601 Q_RETURN_ARG(QScriptValue, result),
571− Q_ARG(const QString&, program),
602+ Q_ARG(const QString&, sourceCode),
572603 Q_ARG(const QString&, fileName),
573604 Q_ARG(int, lineNumber));
574605 return result;
575606 }
576−
577− _evaluatesPending++;
578− QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber);
579− if (hasUncaughtException()) {
580− int line = uncaughtExceptionLineNumber();
581− qCDebug(scriptengine) << "Uncaught exception at (" << _fileNameString << " : " << fileName << ") line" << line << ": " << result.toString();
607+
608+ // Check syntax
609+ const QScriptProgram program(sourceCode, fileName, lineNumber);
610+ if (!hasCorrectSyntax(program)) {
611+ return QScriptValue();
582612 }
583− _evaluatesPending--;
613+
614+ ++_evaluatesPending;
615+ const auto result = QScriptEngine::evaluate(program);
616+ --_evaluatesPending;
617+
618+ const auto hadUncaughtException = hadUncauchtExceptions(*this, program.fileName());
584619 if (_wantSignals) {
585− emit evaluationFinished(result, hasUncaughtException());
620+ emit evaluationFinished(result, hadUncaughtException);
586621 }
587− clearExceptions();
588622 return result;
589623 }
590624
@@ -602,7 +636,7 @@ void ScriptEngine::run() { · 11 unmodified lines
602636 emit runningStateChanged();
603637 }
604638
605− QScriptValue result = evaluate(_scriptContents);
639+ QScriptValue result = evaluate(_scriptContents, _fileNameString);
606640
607641 QElapsedTimer startTime;
608642 startTime.start();
@@ -643,15 +677,6 @@ void ScriptEngine::run() { · 34 unmodified lines
643677 qint64 now = usecTimestampNow();
644678 float deltaTime = (float) (now - lastUpdate) / (float) USECS_PER_SECOND;
645679
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−
655680 if (!_isFinished) {
656681 if (_wantSignals) {
657682 emit update(deltaTime);
@@ -659,6 +684,9 @@ void ScriptEngine::run() { · 1 unmodified line
659684 }
660685 lastUpdate = now;
661686
687+ if (hadUncauchtExceptions(*this, _fileNameString)) {
688+ stop();
689+ }
662690 }
663691
664692 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
896924 }
897925
898926 // Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args
899−void ScriptEngine::generalHandler(const EntityItemID& entityID, const QString& eventName, std::function<QScriptValueList()> argGenerator) {
927+void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs) {
900928 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() << "]";
902930 assert(false);
903931 return;
904932 }
@@ -912,9 +940,8 @@ void ScriptEngine::generalHandler(const EntityItemID& entityID, const QString& e · 7 unmodified lines
912940 }
913941 QScriptValueList handlersForEvent = handlersOnEntity[eventName];
914942 if (!handlersForEvent.isEmpty()) {
915− QScriptValueList args = argGenerator();
916943 for (int i = 0; i < handlersForEvent.count(); ++i) {
917− handlersForEvent[i].call(QScriptValue(), args);
944+ handlersForEvent[i].call(QScriptValue(), eventHanderArgs);
918945 }
919946 }
920947 }
@@ -978,14 +1005,10 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 57 unmodified lines
9781005
9791006 auto scriptCache = DependencyManager::get<ScriptCache>();
9801007 bool isFileUrl = isURL && scriptOrURL.startsWith("file://");
1008+ auto fileName = QString("(EntityID:%1, %2)").arg(entityID.toString(), isURL ? scriptOrURL : "EmbededEntityScript");
9811009
982− // first check the syntax of the script contents
983− QScriptSyntaxCheckResult syntaxCheck = QScriptEngine::checkSyntax(contents);
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;
1010+ QScriptProgram program(contents, fileName);
1011+ if (!hasCorrectSyntax(program)) {
9891012 if (!isFileUrl) {
9901013 scriptCache->addScriptToBadScriptList(scriptOrURL);
9911014 }
@@ -997,12 +1020,15 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 5 unmodified lines
9971020 }
9981021
9991022 QScriptEngine sandbox;
1000− QScriptValue testConstructor = sandbox.evaluate(contents);
1001−
1023+ QScriptValue testConstructor = sandbox.evaluate(program);
1024+ if (hadUncauchtExceptions(sandbox, program.fileName())) {
1025+ return;
1026+ }
1027+
10021028 if (!testConstructor.isFunction()) {
1003− qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID;
1004− qCDebug(scriptengine) << " NOT CONSTRUCTOR";
1005− qCDebug(scriptengine) << " SCRIPT:" << scriptOrURL;
1029+ qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID << "\n"
1030+ " NOT CONSTRUCTOR\n"
1031+ " SCRIPT:" << scriptOrURL;
10061032 if (!isFileUrl) {
10071033 scriptCache->addScriptToBadScriptList(scriptOrURL);
10081034 }
@@ -1015,7 +1041,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 6 unmodified lines
10151041 lastModified = (quint64)QFileInfo(file).lastModified().toMSecsSinceEpoch();
10161042 }
10171043
1018− QScriptValue entityScriptConstructor = evaluate(contents);
1044+ QScriptValue entityScriptConstructor = evaluate(contents, fileName);
10191045 QScriptValue entityScriptObject = entityScriptConstructor.construct();
10201046 EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified };
10211047 _entityScripts[entityID] = newDetails;