Add more Doxygen docs

Doesn't work right in ScriptManager, looks like a conflict with JSDoc
This commit is contained in:
Dale Glass 2023-02-23 23:45:36 +01:00 committed by ksuprynowicz
parent 887ebd5365
commit e35aa8046d
5 changed files with 265 additions and 30 deletions

View file

@ -5,6 +5,7 @@
// 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
@ -44,48 +45,126 @@ template <typename T>
inline T scriptvalue_cast(const ScriptValue& value);
/// [ScriptInterface] Provides an engine-independent interface for QScriptEngine
/**
* @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.
*/
class ScriptEngine {
public:
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 {
QtOwnership = 0,
ScriptOwnership = 1,
AutoOwnership = 2,
QtOwnership = 0, /** Object is managed by Qt */
ScriptOwnership = 1, /** Object is managed by the script */
AutoOwnership = 2, /** Ownership is determined automatically */
};
/**
* @brief Which part of an object is exposed to the script
*
*/
enum QObjectWrapOption {
//ExcludeChildObjects = 0x0001, // The script object will not expose child objects as properties.
ExcludeSuperClassMethods = 0x0002, // The script object will not expose signals and slots inherited from the superclass.
ExcludeSuperClassProperties = 0x0004, // The script object will not expose properties inherited from the superclass.
//ExcludeChildObjects = 0x0001, /** The script object will not expose child objects as properties. */
ExcludeSuperClassMethods = 0x0002, /** The script object will not expose signals and slots inherited from the superclass. */
ExcludeSuperClassProperties = 0x0004, /** The script object will not expose properties inherited from the superclass. */
ExcludeSuperClassContents = ExcludeSuperClassMethods | ExcludeSuperClassProperties,
//ExcludeDeleteLater = 0x0010, // The script object will not expose the QObject::deleteLater() slot.
ExcludeSlots = 0x0020, // The script object will not expose the QObject's slots.
AutoCreateDynamicProperties = 0x0100, // 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.
PreferExistingWrapperObject = 0x0200, // If a wrapper object with the requested configuration already exists, return that object.
SkipMethodsInEnumeration = 0x0008, // Don't include methods (signals and slots) when enumerating the object's properties.
//ExcludeDeleteLater = 0x0010, /** The script object will not expose the QObject::deleteLater() slot. */
ExcludeSlots = 0x0020, /** The script object will not expose the QObject's slots. */
AutoCreateDynamicProperties = 0x0100, /** 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. */
PreferExistingWrapperObject = 0x0200, /** If a wrapper object with the requested configuration already exists, return that object. */
SkipMethodsInEnumeration = 0x0008, /** Don't include methods (signals and slots) when enumerating the object's properties. */
};
Q_DECLARE_FLAGS(QObjectWrapOptions, QObjectWrapOption);
public:
/**
* @brief Stops the currently running script
*
*/
virtual void abortEvaluation() = 0;
/**
* @brief Clears uncaughtException and related
*
*/
virtual void clearExceptions() = 0;
virtual ScriptValue cloneUncaughtException(const QString& detail = QString()) = 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 evaluaate
* @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 namespace, containing all the public APIs
*
* @return ScriptValue
*/
virtual ScriptValue globalObject() {
Q_ASSERT(false);
return ScriptValue();
}
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;
virtual ScriptValue checkScriptSyntax(ScriptProgramPointer program) = 0;
virtual ScriptValue checkScriptSyntax(ScriptProgramPointer program) = 0;
virtual ScriptValue makeError(const ScriptValue& other = ScriptValue(), const QString& type = "Error") = 0;
virtual ScriptManager* manager() const = 0;
virtual bool maybeEmitUncaughtException(const QString& debugHint = QString()) = 0;
@ -107,6 +186,15 @@ public:
virtual ScriptValue newValue(const char* value) = 0;
virtual ScriptValue newVariant(const QVariant& value) = 0;
virtual ScriptValue nullValue() = 0;
/**
* @brief Causes an exception to be raised in the currently executing script
*
* @param exception
* @return true
* @return false
*/
virtual bool raiseException(const ScriptValue& exception) = 0;
virtual void registerEnum(const QString& enumName, QMetaEnum newEnum) = 0;
virtual void registerFunction(const QString& name, FunctionSignature fun, int numArguments = -1) = 0;

View file

@ -112,6 +112,8 @@ public:
#define STATIC_SCRIPT_INITIALIZER(init) \
static ScriptManager::StaticInitializerNode static_script_initializer_(init);
/**jsdoc
* The <code>Script</code> API provides facilities for working with scripts.
*
@ -144,6 +146,21 @@ public:
* <em>Read-only.</em>
* @property {Script.ResourceBuckets} ExternalPaths - External resource buckets.
*/
/**
* @brief Manages a single scripting engine
*
* This class manages and sets up a single scripting engine to make it execute scripts.
*
* It passes the objects needed to expose the public API, provides console access and error
* reporting and event management.
*
* This manipulates a single underlying instance of ScriptEngine.
*
* TODO: Split this class apart, create a ScriptManagerScriptingInterface to handle the JS API
* side separately.
*
*/
class ScriptManager : public QObject, public EntitiesScriptEngineProvider, public std::enable_shared_from_this<ScriptManager> {
Q_OBJECT
Q_PROPERTY(QString context READ getContext)
@ -153,6 +170,10 @@ public:
static const QString SCRIPT_EXCEPTION_FORMAT;
static const QString SCRIPT_BACKTRACE_SEP;
/**
* @brief Context of the script
*
*/
enum Context {
CLIENT_SCRIPT,
ENTITY_CLIENT_SCRIPT,
@ -190,16 +211,34 @@ public:
inline StaticTypesInitializerNode(ScriptManagerInitializer&& pInit) : init(std::move(pInit)),prev(nullptr) { registerNewStaticTypesInitializer(this); }
};
static void registerNewStaticTypesInitializer(StaticTypesInitializerNode* dest);
/// run the script in a dedicated thread. This will have the side effect of evalulating
/// the current script contents and calling run(). Callers will likely want to register the script with external
/// services before calling this.
/**
* @brief Run the script in a dedicated thread
*
* This will have the side effect of evaluating the current script contents and calling run().
* Callers will likely want to register the script with external services before calling this.
*/
void runInThread();
/// run the script in the callers thread, exit when stop() is called.
/**
* @brief Run the script in the caller's thread, exit when stop() is called.
*
*/
void run();
/**
* @brief Get the filename of the running script
*
* @return QString Filename
*/
QString getFilename() const;
/**
* @brief Underlying scripting engine
*
* @return ScriptEnginePointer Scripting engine
*/
inline ScriptEnginePointer engine() { return _engine; }
QList<EntityItemID> getListOfEntityScriptIDs();
@ -225,16 +264,29 @@ public:
// NOTE - this is intended to be a public interface for Agent scripts, and local scripts, but not for EntityScripts
Q_INVOKABLE void stop(bool marshal = false);
// Stop any evaluating scripts and wait for the scripting thread to finish.
/**
* @brief Stop any evaluating scripts and wait for the scripting thread to finish.
*
* @param shutdown True if we are currently shutting down
*/
void waitTillDoneRunning(bool shutdown = false);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// NOTE - these are NOT intended to be public interfaces available to scripts, the are only Q_INVOKABLE so we can
// properly ensure they are only called on the correct thread
/// if the script engine is not already running, this will download the URL and start the process of seting it up
/// to run... NOTE - this is used by Application currently to load the url. We don't really want it to be exposed
/// to scripts. we may not need this to be invokable
/**
* @brief Load a script from a given URL
*
* If the script engine is not already running, this will download the URL and start the process of seting it up
* to run.
* @note This is used by Application currently to load the url. We don't really want it to be exposed
* to scripts. we may not need this to be invokable
*
* @param scriptURL URL where to load the script from
* @param reload
*/
void loadURL(const QUrl& scriptURL, bool reload);
bool hasValidScriptSuffix(const QString& scriptFileName);
@ -648,9 +700,26 @@ public:
void disconnectNonEssentialSignals();
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// These are currently used by Application to track if a script is user loaded or not. Consider finding a solution
// inside of Application so that the ScriptManager class is not polluted by this notion
/**
* @brief Set whether this script was user-loaded
*
* This is used by Application to track if a script is user loaded or not.
* @note Consider finding a solution inside of Application so that the ScriptManager class is not polluted by this notion
*
* @param isUserLoaded Script is user-loaded.
*/
void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; }
/**
* @brief Whether the script was user-loaded.
*
* This is used by Application to track if a script is user loaded or not.
* @note Consider finding a solution inside of Application so that the ScriptManager class is not polluted by this notion
*
* @return true
* @return false
*/
bool isUserLoaded() const { return _isUserLoaded; }
void setQuitWhenFinished(const bool quitWhenFinished) { _quitWhenFinished = quitWhenFinished; }
@ -669,10 +738,21 @@ public:
void setScriptEngines(QSharedPointer<ScriptEngines>& scriptEngines) { _scriptEngines = scriptEngines; }
// call all the registered event handlers on an entity for the specified name.
/**
* @brief Call all the registered event handlers on an entity for the specified name.
*
* @param entityID
* @param eventName
* @param eventHanderArgs
*/
void forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, const ScriptValueList& eventHanderArgs);
// remove all event handlers for the specified entityID (i.e. the entity is being removed)
/**
* @brief Remove all event handlers for the specified entityID (i.e. the entity is being removed)
*
* @param entityID
*/
void removeAllEventHandlers(const EntityItemID& entityID);

View file

@ -4,6 +4,7 @@
//
// Created by Heather Anderson on 5/2/21.
// Copyright 2021 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
@ -22,32 +23,93 @@ class ScriptSyntaxCheckResult;
using ScriptProgramPointer = std::shared_ptr<ScriptProgram>;
using ScriptSyntaxCheckResultPointer = std::shared_ptr<ScriptSyntaxCheckResult>;
/// [ScriptInterface] Provides an engine-independent interface for QScriptProgram
/**
* @brief Engine-independent representation of a script program
*
* This is an analog of QScriptProgram from Qt5.
*
* It's used to pre-compile scripts, and to check their syntax.
*
*/
class ScriptProgram {
public:
virtual ScriptSyntaxCheckResultPointer checkSyntax() = 0; //It cannot be const anymore because V8 doesn't have separate syntax checking function
/**
* @brief Returns the filename associated with this program.
*
* @return QString
*/
virtual QString fileName() const = 0;
/**
* @brief Returns the source code of this program.
*
* @return QString
*/
virtual QString sourceCode() const = 0;
protected:
~ScriptProgram() {} // prevent explicit deletion of base class
};
/// [ScriptInterface] Provides an engine-independent interface for QScriptSyntaxCheckResult
/**
* @brief Engine-independent representation of a script syntax check
*
* This is an analog of QScriptSyntaxCheckResult from Qt5.
*
*
*/
class ScriptSyntaxCheckResult {
public:
/**
* @brief State of the syntax check
*
*/
enum State
{
Error = 0,
Intermediate = 1,
Valid = 2
Error = 0, /** The program contains a syntax error. */
Intermediate = 1, /** The program is incomplete. */
Valid = 2 /** The program is a syntactically correct program. */
};
public:
/**
* @brief Returns the error column number of this ScriptSyntaxCheckResult, or -1 if there is no error.
*
* @return int
*/
virtual int errorColumnNumber() const = 0;
/**
* @brief Returns the error line number of this ScriptSyntaxCheckResult, or -1 if there is no error.
*
* @return int
*/
virtual int errorLineNumber() const = 0;
/**
* @brief Returns the error message of this ScriptSyntaxCheckResult, or an empty string if there is no error.
*
* @return QString
*/
virtual QString errorMessage() const = 0;
/**
* @brief
*
* @return QString
*/
virtual QString errorBacktrace() const = 0;
/**
* @brief Returns the state of this ScriptSyntaxCheckResult.
*
* @return State
*/
virtual State state() const = 0;
protected:

View file

@ -23,6 +23,9 @@
#include "ScriptEngineV8.h"
#include "V8Types.h"
// V8TODO: This class is likely unnecessary, and it'd be enough
// to just use a non-abstract version of ScriptSyntaxCheckResult instead.
class ScriptSyntaxCheckResultV8Wrapper final : public ScriptSyntaxCheckResult {
public: // construction
inline ScriptSyntaxCheckResultV8Wrapper() : _errorColumnNumber(0), _errorLineNumber(0), _errorMessage("Not compiled"), _state(ScriptSyntaxCheckResult::Error) {}

View file

@ -102,4 +102,6 @@ void ScriptEngineTests::scriptTest() {
//ac->shutdownScripting();
//TODO: Add a test for Script.require(JSON)
}