2763 lines · 116.2 KB · C++
RawDownload
 #ifdef DEBUG_ENGINE_STATE    _debugDump(        "---- CURRENT STATE OF ENGINE: --------------------------",        globalObject(),        "--------------------------------------------------------"    );#endif // DEBUG_ENGINE_STATE} void ScriptManager::refreshFileScript(const EntityItemID& entityID) {    if (!HIFI_AUTOREFRESH_FILE_SCRIPTS || !hasEntityScriptDetails(entityID)) {        return;    }     static bool recurseGuard = false;    if (recurseGuard) {        return;    }    recurseGuard = true;     EntityScriptDetails details;    {        QWriteLocker locker { &_entityScriptsLock };        details = _entityScripts[entityID];    }    // Check to see if a file based script needs to be reloaded (easier debugging)    if (details.lastModified > 0) {        QString filePath = QUrl(details.scriptText).toLocalFile();        auto lastModified = QFileInfo(filePath).lastModified().toMSecsSinceEpoch();        if (lastModified > details.lastModified) {            scriptInfoMessage("Reloading modified script " + details.scriptText, filePath, -1);            loadEntityScript(entityID, details.scriptText, true);        }    }    recurseGuard = false;} // Execute operation in the appropriate context for (the possibly empty) entityID.// Even if entityID is supplied as currentEntityIdentifier, this still documents the source// of the code being executed (e.g., if we ever sandbox different entity scripts, or provide different// global values for different entity scripts).void ScriptManager::doWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, std::function<void()> operation) {    EntityItemID oldIdentifier = currentEntityIdentifier;    QUrl oldSandboxURL = currentSandboxURL;    currentEntityIdentifier = entityID;    currentSandboxURL = sandboxURL; #if DEBUG_CURRENT_ENTITY    ScriptValue oldData = this->globalObject().property("debugEntityID");    this->globalObject().setProperty("debugEntityID", entityID.toScriptValue(this)); // Make the entityID available to javascript as a global.    operation();    this->globalObject().setProperty("debugEntityID", oldData);#else    operation();#endif    currentEntityIdentifier = oldIdentifier;    currentSandboxURL = oldSandboxURL;} void ScriptManager::callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, const ScriptValue& function, const ScriptValue& thisObject, const ScriptValueList& args) {    auto operation = [&]() {        function.call(thisObject, args);    };    doWithEnvironment(entityID, sandboxURL, operation);} void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params, const QUuid& remoteCallerID) {    if (QThread::currentThread() != thread()) {#ifdef THREAD_DEBUGGING        qCDebug(scriptengine) << "*** WARNING *** ScriptManager::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]  "            "entityID:" << entityID << "methodName:" << methodName;#endif         QMetaObject::invokeMethod(this, "callEntityScriptMethod",                                  Q_ARG(const EntityItemID&, entityID),                                  Q_ARG(const QString&, methodName),                                  Q_ARG(const QStringList&, params),                                  Q_ARG(const QUuid&, remoteCallerID));        return;    }#ifdef THREAD_DEBUGGING    qCDebug(scriptengine) << "ScriptManager::callEntityScriptMethod() called on correct thread [" << thread() << "]  "        "entityID:" << entityID << "methodName:" << methodName;#endif     if (HIFI_AUTOREFRESH_FILE_SCRIPTS && methodName != "unload") {        refreshFileScript(entityID);    }    if (isEntityScriptRunning(entityID)) {        EntityScriptDetails details;        {            QWriteLocker locker { &_entityScriptsLock };            details = _entityScripts[entityID];        }        ScriptValue entityScript = details.scriptObject;  // previously loaded         // If this is a remote call, we need to check to see if the function is remotely callable        // we do this by checking for the existance of the 'remotelyCallable' property on the        // entityScript. And we confirm that the method name is included. If this fails, the        // function will not be called.        bool callAllowed = false;        if (remoteCallerID == QUuid()) {            callAllowed = true;        } else {            if (entityScript.property("remotelyCallable").isArray()) {                auto callables = entityScript.property("remotelyCallable");                auto callableCount = callables.property("length").toInteger();                for (int i = 0; i < callableCount; i++) {                    auto callable = callables.property(i).toString();                    if (callable == methodName) {                        callAllowed = true;                        break;                    }                }            }            if (!callAllowed) {                qCDebug(scriptengine) << "Method [" << methodName << "] not remotely callable.";            }        }         if (callAllowed && entityScript.property(methodName).isFunction()) {            auto scriptEngine = engine().get();             ScriptValueList args;            args << EntityItemIDtoScriptValue(scriptEngine, entityID);            args << scriptValueFromSequence(scriptEngine, params);             ScriptValue oldData = scriptEngine->globalObject().property("Script").property("remoteCallerID");            scriptEngine->globalObject().property("Script").setProperty("remoteCallerID", remoteCallerID.toString()); // Make the remoteCallerID available to javascript as a global.            callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);            scriptEngine->globalObject().property("Script").setProperty("remoteCallerID", oldData);        }    }} void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event) {    if (QThread::currentThread() != thread()) {#ifdef THREAD_DEBUGGING        qCDebug(scriptengine) << "*** WARNING *** ScriptManager::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]  "            "entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent";#endif         QMetaObject::invokeMethod(this, "callEntityScriptMethod",                                  Q_ARG(const EntityItemID&, entityID),                                  Q_ARG(const QString&, methodName),                                  Q_ARG(const PointerEvent&, event));        return;    }#ifdef THREAD_DEBUGGING    qCDebug(scriptengine) << "ScriptManager::callEntityScriptMethod() called on correct thread [" << thread() << "]  "        "entityID:" << entityID << "methodName:" << methodName << "event: pointerEvent";#endif     if (HIFI_AUTOREFRESH_FILE_SCRIPTS) {        refreshFileScript(entityID);    }    if (isEntityScriptRunning(entityID)) {        EntityScriptDetails details;        {            QWriteLocker locker { &_entityScriptsLock };            details = _entityScripts[entityID];        }        ScriptValue entityScript = details.scriptObject;  // previously loaded        if (entityScript.property(methodName).isFunction()) {            auto scriptEngine = engine().get();             ScriptValueList args;            args << EntityItemIDtoScriptValue(scriptEngine, entityID);            args << event.toScriptValue(scriptEngine);            callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);        }    }} void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision) {    if (QThread::currentThread() != thread()) {#ifdef THREAD_DEBUGGING        qCDebug(scriptengine) << "*** WARNING *** ScriptManager::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]  "            "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";#endif         QMetaObject::invokeMethod(this, "callEntityScriptMethod",                                  Q_ARG(const EntityItemID&, entityID),                                  Q_ARG(const QString&, methodName),                                  Q_ARG(const EntityItemID&, otherID),                                  Q_ARG(const Collision&, collision));        return;    }#ifdef THREAD_DEBUGGING    qCDebug(scriptengine) << "ScriptManager::callEntityScriptMethod() called on correct thread [" << thread() << "]  "        "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";#endif     if (HIFI_AUTOREFRESH_FILE_SCRIPTS) {        refreshFileScript(entityID);    }    if (isEntityScriptRunning(entityID)) {        EntityScriptDetails details;        {            QWriteLocker locker { &_entityScriptsLock };            details = _entityScripts[entityID];        }        ScriptValue entityScript = details.scriptObject;  // previously loaded        if (entityScript.property(methodName).isFunction()) {            auto scriptEngine = engine().get();             ScriptValueList args;            args << EntityItemIDtoScriptValue(scriptEngine, entityID);            args << EntityItemIDtoScriptValue(scriptEngine, otherID);            args << collisionToScriptValue(scriptEngine, collision);            callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);        }    }} QString ScriptManager::getExternalPath(ExternalResource::Bucket bucket, const QString& path) {    return ExternalResource::getInstance()->getUrl(bucket, path);} QString ScriptManager::formatException(const ScriptValue& exception, bool includeExtendedDetails) {    if (!_engine->IS_THREADSAFE_INVOCATION(__FUNCTION__)) {        return QString();    }    QString note{ "UncaughtException" };    QString result;     if (!exception.isObject()) {        return result;    }    const auto message = exception.toString();    const auto fileName = exception.property("fileName").toString();    const auto lineNumber = exception.property("lineNumber").toString();    const auto stacktrace = exception.property("stack").toString();     if (includeExtendedDetails) {        // Display additional exception / troubleshooting hints that can be added via the custom Error .detail property        // Example difference:        //   [UncaughtExceptions] Error: Can't find variable: foobar in atp:/myentity.js\n...        //   [UncaughtException (construct {1eb5d3fa-23b1-411c-af83-163af7220e3f})] Error: Can't find variable: foobar in atp:/myentity.js\n...        if (exception.property("detail").isValid()) {            note += " " + exception.property("detail").toString();        }    }     result = QString(SCRIPT_EXCEPTION_FORMAT).arg(note, message, fileName, lineNumber);    if (!stacktrace.isEmpty()) {        result += QString("\n[Backtrace]%1%2").arg(SCRIPT_BACKTRACE_SEP).arg(stacktrace);    }    return result;} ScriptValue ScriptManager::evaluate(const QString& program, const QString& fileName) {    return _engine->evaluate(program, fileName);} void ScriptManager::requestGarbageCollection() {    _engine->requestCollectGarbage();} void ScriptManager::logBacktrace(const QString &title) {    _engine->logBacktrace(title);}

Lines 2501–2763 of 2763. Previous lines