535 lines · 18.3 KB · C
RawDownload
////  ScriptEngine.h//  libraries/script-engine/src////  Created by Brad Hefta-Gaub on 12/14/13.//  Copyright 2013 High Fidelity, Inc.//  Copyright 2020 Vircadia contributors.//  Copyright 2023 Overte e.V.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html//  SPDX-License-Identifier: Apache-2.0// /// @addtogroup ScriptEngine/// @{ #ifndef hifi_ScriptEngine_h#define hifi_ScriptEngine_h #include <memory> #include <QtCore/QFlags>#include <QtCore/QObject> #include "ScriptValue.h"#include "ScriptException.h" // These are used for debugging memory leaks caused by persistent handles//#define OVERTE_V8_MEMORY_DEBUG class QByteArray;class QLatin1String;class QString;class QThread;class QVariant;class ScriptContext;class ScriptEngine;class ScriptManager;class ScriptProgram;using ScriptEnginePointer = std::shared_ptr<ScriptEngine>;using ScriptProgramPointer = std::shared_ptr<ScriptProgram>; Q_DECLARE_METATYPE(ScriptEnginePointer); template <typename T>inline ScriptValue scriptValueFromValue(ScriptEngine* engine, const T& t); template <typename T>inline T scriptvalue_cast(const ScriptValue& value); class ScriptEngineMemoryStatistics {public:    size_t totalHeapSize;    size_t usedHeapSize;    size_t totalAvailableSize;    size_t totalGlobalHandlesSize;    size_t usedGlobalHandlesSize;#ifdef OVERTE_V8_MEMORY_DEBUG    size_t scriptValueCount;    size_t scriptValueProxyCount;    size_t qObjectCount;    //size_t qVariantProxyCount;#endif}; /** * @brief Provides an engine-independent interface for a scripting engine * * Each script engine is strictly single threaded. * * This class only provides an interface to the underlying scripting engine, and doesn't * provide the full environment needed to execute scripts. * * To execute scripts that have access to the API, use ScriptManager. * * Exception handling * ================== * * Exceptions are handled in two directions: exceptions thrown by the code executing in the scripting * engine, but not captured by the running code are caught by this object and can be inspected. * * If an exception in the running code occurs, then the exception() signal is emitted. Also, * hasUncaughtException() returns true, and uncaughException() returns the ScriptException with the * details. Both the signal and uncaughtException() return the same information, and either can * be used depending on what best fits the program. * * To inject an exception into the running script, use raiseException(). This may result in the script * not capturing it and an uncaughtException happening as a result. */class ScriptEngine : public QObject {    Q_OBJECTpublic:     ScriptEngine(ScriptManager *manager = nullptr) : _manager(manager) {     }     typedef ScriptValue (*FunctionSignature)(ScriptContext*, ScriptEngine*);    typedef ScriptValue (*MarshalFunction)(ScriptEngine*, const void*);    typedef bool (*DemarshalFunction)(const ScriptValue&, QVariant &dest);     /**     * @brief Who owns a given object     *     */    enum ValueOwnership {        /**         * @brief Object is managed by Qt         *         */        QtOwnership = 0,         /**         * @brief Object is managed by the script         *         */        ScriptOwnership = 1,         /**         * @brief Ownership is determined automatically.         * If the object has a parent, it's deemed QtOwnership.         * If the object has no parent, it's deemed ScriptOwnership.         *         */        AutoOwnership = 2,    };     /**     * @brief Which part of an object is exposed to the script     *     */    enum QObjectWrapOption {         /**         * @brief The script object will not expose child objects as properties.         *         */        //ExcludeChildObjects	= 0x0001,         /**         * @brief The script object will not expose signals and slots inherited from the superclass.         *         */        ExcludeSuperClassMethods = 0x0002,         /**         * @brief The script object will not expose properties inherited from the superclass.         *         */        ExcludeSuperClassProperties	= 0x0004,         /**         * @brief The script object will not expose the QObject::deleteLater() slot.         *         */        ExcludeSuperClassContents = ExcludeSuperClassMethods | ExcludeSuperClassProperties,         //ExcludeDeleteLater = 0x0010,         /**         * @brief The script object will not expose the QObject's slots.         *         */        ExcludeSlots = 0x0020,         /**         * @brief Properties that don't already exist in the QObject will be created as dynamic properties of that object, rather than as properties of the script object.         *         */        AutoCreateDynamicProperties = 0x0100,         /**         * @brief If a wrapper object with the requested configuration already exists, return that object.         *         */        PreferExistingWrapperObject = 0x0200,         /**         * @brief Don't include methods (signals and slots) when enumerating the object's properties.         *         */        SkipMethodsInEnumeration = 0x0008,    };    Q_DECLARE_FLAGS(QObjectWrapOptions, QObjectWrapOption); public:     /**     * @brief Class used as a base for script engine-specific thread safety guards.     *     * Constructing the guard while holding the guard in the same thread must be possible.     *     */    class ScriptEngineScopeGuard {    protected:        ScriptEngineScopeGuard() = default;         // prevent copy and move        ScriptEngineScopeGuard(const ScriptEngineScopeGuard&) = delete;        ScriptEngineScopeGuard(ScriptEngineScopeGuard&&) = delete;        ScriptEngineScopeGuard& operator=(const ScriptEngineScopeGuard&) = delete;        ScriptEngineScopeGuard& operator=(ScriptEngineScopeGuard&&) = delete;    public:        virtual ~ScriptEngineScopeGuard() = default;    };     /**     * @brief Creates a thread safety scope guard     *     * A scope guard needs to be created before any script engine access and destroyed afterwards.     * This function is thread safe, if a scope guard is being created while another thread is using the script engine,     * function will block until the scope guard on another thread is destroyed.     *     * Constructing the guard while holding the guard in the same thread is possible.     *     * @return Smart pointer to newly created scope guard     */    virtual std::unique_ptr<ScriptEngineScopeGuard> getScopeGuard() = 0;     /**     * @brief Stops the currently running script     *     */    virtual void abortEvaluation() = 0;     /**     * @brief Clears uncaughtException and related     *     */    virtual void clearExceptions() = 0;     /**     * @brief Context of the currently running script     *     * This allows getting a backtrace, the local variables of the currently running function, etc.     *     * @return ScriptContext*     */    virtual ScriptContext* currentContext() const = 0;     /**     * @brief Runs a script     *     * This may be called several times during the lifetime of a scripting engine, with the     * side effects accumulating.     *     * @param program Code to run     * @param fileName Name of the script, for informational purposes     * @return ScriptValue Return value of the script when it finishes running.     */    virtual ScriptValue evaluate(const QString& program, const QString& fileName = QString()) = 0;     /**     * @brief Evaluates a pre-compiled program     *     * @param program Program to evaluate     * @return ScriptValue     */    virtual ScriptValue evaluate(const ScriptProgramPointer &program) = 0;      /**     * @brief Evaluate a script in a separate environment     *     * Used for evaluating included scripts     *     * @param locals Local variables available to the script     * @param program Code to run     * @return ScriptValue     */    virtual ScriptValue evaluateInClosure(const ScriptValue& locals, const ScriptProgramPointer& program) = 0;     /**     * @brief Global object which holds all the functions and variables available everywhere     *     * This is a JavaScript concept, https://javascript.info/global-object     *     * @note This may not belong in the base class.     * @return ScriptValue Global Object     */    virtual ScriptValue globalObject() {        Q_ASSERT(false);        return ScriptValue();    }     /**     * @brief Whether the script has an uncaught exception     *     * @return true There is an uncaught exception     * @return false There's no exception     */    virtual bool hasUncaughtException() const = 0;     /**     * @brief Whether a script is currently being evaluated     *     * @return true A script is currently being evaluated     * @return false No script is being evaluated     */    virtual bool isEvaluating() const = 0;    //virtual ScriptValue lintScript(const QString& sourceCode, const QString& fileName, const int lineNumber = 1) = 0;     /**     * @brief Check a program for syntax errors     *     * Returns an object with at least the following properties:     * * fileName     * * lineNumber     * * stack     * * formatted     *     * @param program Program to check     * @return ScriptValue Result     *     * @note It could be a good improvement to redo this to return a struct instead.     */    virtual ScriptValue checkScriptSyntax(ScriptProgramPointer program) = 0;     /**     * @brief Pointer to the ScriptManager that controls this scripting engine     *     * @return ScriptManager* ScriptManager     */    ScriptManager* manager() const { return _manager; }     virtual ScriptValue newArray(uint length = 0) = 0;    virtual ScriptValue newArrayBuffer(const QByteArray& message) = 0;    virtual ScriptValue newFunction(FunctionSignature fun, int length = 0) {        Q_ASSERT(false);        return ScriptValue();    }    virtual ScriptValue newObject() = 0;    virtual ScriptProgramPointer newProgram(const QString& sourceCode, const QString& fileName) = 0;    virtual ScriptValue newQObject(QObject *object, ValueOwnership ownership = QtOwnership, const QObjectWrapOptions &options = QObjectWrapOptions()) = 0;    virtual ScriptValue newValue(bool value) = 0;    virtual ScriptValue newValue(int value) = 0;    virtual ScriptValue newValue(uint value) = 0;    virtual ScriptValue newValue(double value) = 0;    virtual ScriptValue newValue(const QString& value) = 0;    virtual ScriptValue newValue(const QLatin1String& value) = 0;    virtual ScriptValue newValue(const char* value) = 0;    virtual ScriptValue newVariant(const QVariant& value) = 0;    virtual ScriptValue nullValue() = 0;      /**     * @brief Make a ScriptValue that contains an error     *     * This is used to throw an error inside the running script     *     * @param other     * @param type     * @return ScriptValue ScriptValue containing error     */    virtual ScriptValue makeError(const ScriptValue& other, const QString& type = "Error") = 0;      /**     * @brief Causes an exception to be raised in the currently executing script     *     * @param exception Exception to be thrown in the script     * @param reason Explanatory text about why the exception happened, for logging     * @return true Exception was successfully thrown     * @return false Exception couldn't be thrown because no script is running     */    virtual bool raiseException(const ScriptValue& exception, const QString &reason = QString()) = 0;     /**     * @brief Causes an exception to be raised in the currently executing script     *     * @param error Exception to be thrown in the script     * @param reason Explanatory text about why the exception happened, for logging     * @return true Exception was successfully thrown     * @return false Exception couldn't be thrown because no script is running     */    virtual bool raiseException(const QString& error, const QString &reason = QString()) = 0;      virtual void registerEnum(ScriptEngineScopeGuard* scopeGuard, const QString& enumName, QMetaEnum newEnum) = 0;    virtual void registerFunction(ScriptEngineScopeGuard* scopeGuard, const QString& name, FunctionSignature fun, int numArguments = -1) = 0;    virtual void registerFunction(ScriptEngineScopeGuard* scopeGuard, const QString& parent, const QString& name, FunctionSignature fun, int numArguments = -1) = 0;    virtual void registerGetterSetter(ScriptEngineScopeGuard* scopeGuard, const QString& name, FunctionSignature getter, FunctionSignature setter, const QString& parent = QString("")) = 0;    virtual void registerGlobalObject(ScriptEngineScopeGuard* scopeGuard, const QString& name, QObject* object, ScriptEngine::ValueOwnership = ScriptEngine::QtOwnership) = 0;    virtual void setDefaultPrototype(int metaTypeId, const ScriptValue& prototype) = 0;    virtual void setObjectName(const QString& name) = 0;    virtual bool setProperty(const char* name, const QVariant& value) = 0;    virtual void setProcessEventsInterval(int interval) = 0;    virtual QThread* thread() const = 0;    virtual void setThread(QThread* thread) = 0;    //Q_INVOKABLE virtual void enterIsolateOnThisThread() = 0;    virtual ScriptValue undefinedValue() = 0;     /**     * @brief Last uncaught exception, if any.     *     * The returned shared pointer is newly allocated by the function,     * and modifying it has no effect on the internal state of the ScriptEngine.     *     * @return std::shared_ptr<ScriptValue> Uncaught exception from the script     */    virtual std::shared_ptr<ScriptException> uncaughtException() const = 0;     virtual void updateMemoryCost(const qint64& deltaSize) = 0;    virtual void requestCollectGarbage() = 0;     /**     * @brief Tells the script engine to process any events it may have queued internally.     * Used by V8 for async functions and WebAssembly compilation.     */    virtual void processEvents() = 0;     /**     * @brief Test the underlying scripting engine     *     * This compiles, executes and verifies the execution of a trivial test program     * to make sure the underlying scripting engine actually works.     *     * @deprecated Test function, not part of the API, can be removed     */    virtual void compileTest() = 0;    virtual QString scriptValueDebugDetails(const ScriptValue &value) = 0;    virtual QString scriptValueDebugListMembers(const ScriptValue &value) = 0;     /**     * @brief Log the current backtrace     *     * Logs the current backtrace for debugging     *     * @param title Informative title for the backtrace     */    virtual void logBacktrace(const QString &title) = 0;     /**     * @brief Return memory usage statistics data.     *     * Returns memory usage statistics data for debugging.     *     * @return ScriptEngineMemoryStatistics Object containing memory usage statistics data.     */    virtual ScriptEngineMemoryStatistics getMemoryUsageStatistics() = 0;     /**     * @brief Start collecting object statistics that can later be reported with dumpHeapObjectStatistics().     */    virtual void startCollectingObjectStatistics() = 0;     /**     * @brief Prints heap statistics to a file. Collecting needs to first be started with dumpHeapObjectStatistics().     */    virtual void dumpHeapObjectStatistics() = 0;     /**     * @brief Starts collecting profiling data.     */    virtual void startProfiling() = 0;     /**     * @brief Stops collecting profiling data and saves it to a CSV file in Logs directory.     */    virtual void stopProfilingAndSave() = 0;     /**     * @brief Cleanup function that disconnects signals connected to script proxies to avoid use-after-delete crash when shutting down script engine.     */    virtual void disconnectSignalProxies() = 0; public:    // helper to detect and log warnings when other code invokes QScriptEngine/BaseScriptEngine in thread-unsafe ways    bool IS_THREADSAFE_INVOCATION(const QString& method); public:    template <typename T>    inline T fromScriptValue(const ScriptValue& value) {        return scriptvalue_cast<T>(value);    }     template <typename T>    inline ScriptValue toScriptValue(const T& value) {        return scriptValueFromValue(this, value);    } public: // not for public use, but I don't like how Qt strings this along with private friend functions    virtual ScriptValue create(int type, const void* ptr) = 0;    virtual QVariant convert(const ScriptValue& value, int type) = 0;    virtual void registerCustomType(int type, MarshalFunction mf, DemarshalFunction df) = 0;    virtual QStringList getCurrentScriptURLs() const = 0;    virtual void perManagerLoopIterationCleanup() = 0; signals:    /**     * @brief The script being run threw an exception     *     * @param exception Exception that was thrown     */    void exception(std::shared_ptr<ScriptException> exception); protected:

Lines 1–500 of 535.