mirror of
https://github.com/overte-org/overte.git
synced 2025-04-14 07:47:30 +02:00
Initial exception handling
Make exception handling use the new ScriptException class. Add exception signal Throw exceptions in script evaluation Remove references to ScriptManager from ScriptEngine
This commit is contained in:
parent
2bcd1106d1
commit
c0e62c5cc2
8 changed files with 297 additions and 92 deletions
|
@ -30,7 +30,7 @@ QList<EntityItemID> EntityEditFilters::getZonesByPosition(glm::vec3& position) {
|
||||||
for (auto id : zoneIDs) {
|
for (auto id : zoneIDs) {
|
||||||
if (!id.isInvalidID()) {
|
if (!id.isInvalidID()) {
|
||||||
// for now, look it up in the tree (soon we need to cache or similar?)
|
// for now, look it up in the tree (soon we need to cache or similar?)
|
||||||
EntityItemPointer itemPtr = _tree->findEntityByEntityItemID(id);
|
EntityItemPointer itemPtr = _tree->findEntityByEntityItemID(id);
|
||||||
auto zone = std::dynamic_pointer_cast<ZoneEntityItem>(itemPtr);
|
auto zone = std::dynamic_pointer_cast<ZoneEntityItem>(itemPtr);
|
||||||
if (!zone) {
|
if (!zone) {
|
||||||
// TODO: maybe remove later?
|
// TODO: maybe remove later?
|
||||||
|
@ -39,7 +39,7 @@ QList<EntityItemID> EntityEditFilters::getZonesByPosition(glm::vec3& position) {
|
||||||
zones.append(id);
|
zones.append(id);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// the null id is the global filter we put in the domain server's
|
// the null id is the global filter we put in the domain server's
|
||||||
// advanced entity server settings
|
// advanced entity server settings
|
||||||
zones.append(id);
|
zones.append(id);
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ QList<EntityItemID> EntityEditFilters::getZonesByPosition(glm::vec3& position) {
|
||||||
|
|
||||||
bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut,
|
bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut,
|
||||||
bool& wasChanged, EntityTree::FilterType filterType, EntityItemID& itemID, const EntityItemPointer& existingEntity) {
|
bool& wasChanged, EntityTree::FilterType filterType, EntityItemID& itemID, const EntityItemPointer& existingEntity) {
|
||||||
|
|
||||||
// get the ids of all the zones (plus the global entity edit filter) that the position
|
// get the ids of all the zones (plus the global entity edit filter) that the position
|
||||||
// lies within
|
// lies within
|
||||||
auto zoneIDs = getZonesByPosition(position);
|
auto zoneIDs = getZonesByPosition(position);
|
||||||
|
@ -57,12 +57,12 @@ bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& proper
|
||||||
if (!itemID.isInvalidID() && id == itemID) {
|
if (!itemID.isInvalidID() && id == itemID) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the filter pair, etc...
|
// get the filter pair, etc...
|
||||||
_lock.lockForRead();
|
_lock.lockForRead();
|
||||||
FilterData filterData = _filterDataMap.value(id);
|
FilterData filterData = _filterDataMap.value(id);
|
||||||
_lock.unlock();
|
_lock.unlock();
|
||||||
|
|
||||||
if (filterData.valid()) {
|
if (filterData.valid()) {
|
||||||
if (filterData.rejectAll) {
|
if (filterData.rejectAll) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -159,13 +159,13 @@ bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& proper
|
||||||
// otherwise, assume it wants to pass all properties
|
// otherwise, assume it wants to pass all properties
|
||||||
propertiesOut = propertiesIn;
|
propertiesOut = propertiesIn;
|
||||||
wasChanged = false;
|
wasChanged = false;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if we made it here,
|
// if we made it here,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,23 +177,23 @@ void EntityEditFilters::removeFilter(EntityItemID entityID) {
|
||||||
void EntityEditFilters::addFilter(EntityItemID entityID, QString filterURL) {
|
void EntityEditFilters::addFilter(EntityItemID entityID, QString filterURL) {
|
||||||
|
|
||||||
QUrl scriptURL(filterURL);
|
QUrl scriptURL(filterURL);
|
||||||
|
|
||||||
// setting it to an empty string is same as removing
|
// setting it to an empty string is same as removing
|
||||||
if (filterURL.size() == 0) {
|
if (filterURL.size() == 0) {
|
||||||
removeFilter(entityID);
|
removeFilter(entityID);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following should be abstracted out for use in Agent.cpp (and maybe later AvatarMixer.cpp)
|
// The following should be abstracted out for use in Agent.cpp (and maybe later AvatarMixer.cpp)
|
||||||
if (scriptURL.scheme().isEmpty() || (scriptURL.scheme() == HIFI_URL_SCHEME_FILE)) {
|
if (scriptURL.scheme().isEmpty() || (scriptURL.scheme() == HIFI_URL_SCHEME_FILE)) {
|
||||||
qWarning() << "Cannot load script from local filesystem, because assignment may be on a different computer.";
|
qWarning() << "Cannot load script from local filesystem, because assignment may be on a different computer.";
|
||||||
scriptRequestFinished(entityID);
|
scriptRequestFinished(entityID);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// first remove any existing info for this entity
|
// first remove any existing info for this entity
|
||||||
removeFilter(entityID);
|
removeFilter(entityID);
|
||||||
|
|
||||||
// reject all edits until we load the script
|
// reject all edits until we load the script
|
||||||
FilterData filterData;
|
FilterData filterData;
|
||||||
filterData.rejectAll = true;
|
filterData.rejectAll = true;
|
||||||
|
@ -201,7 +201,7 @@ void EntityEditFilters::addFilter(EntityItemID entityID, QString filterURL) {
|
||||||
_lock.lockForWrite();
|
_lock.lockForWrite();
|
||||||
_filterDataMap.insert(entityID, filterData);
|
_filterDataMap.insert(entityID, filterData);
|
||||||
_lock.unlock();
|
_lock.unlock();
|
||||||
|
|
||||||
auto scriptRequest = DependencyManager::get<ResourceManager>()->createResourceRequest(
|
auto scriptRequest = DependencyManager::get<ResourceManager>()->createResourceRequest(
|
||||||
this, scriptURL, true, -1, "EntityEditFilters::addFilter");
|
this, scriptURL, true, -1, "EntityEditFilters::addFilter");
|
||||||
if (!scriptRequest) {
|
if (!scriptRequest) {
|
||||||
|
@ -232,18 +232,19 @@ static bool hasCorrectSyntax(const ScriptProgramPointer& program) {
|
||||||
}
|
}
|
||||||
static bool hadUncaughtExceptions(ScriptEngine& engine, const QString& fileName) {
|
static bool hadUncaughtExceptions(ScriptEngine& engine, const QString& fileName) {
|
||||||
if (engine.hasUncaughtException()) {
|
if (engine.hasUncaughtException()) {
|
||||||
const auto backtrace = engine.uncaughtExceptionBacktrace();
|
//const auto backtrace = engine.uncaughtException().backtrace;
|
||||||
const auto exception = engine.uncaughtException().toString();
|
//const auto exception = engine.uncaughtException().toString();
|
||||||
const auto line = QString::number(engine.uncaughtExceptionLineNumber());
|
//const auto line = QString::number(engine.uncaughtExceptionLineNumber());
|
||||||
|
qCritical() << engine.uncaughtException();
|
||||||
engine.clearExceptions();
|
engine.clearExceptions();
|
||||||
|
|
||||||
static const QString SCRIPT_EXCEPTION_FORMAT = "[UncaughtException] %1 in %2:%3";
|
//static const QString SCRIPT_EXCEPTION_FORMAT = "[UncaughtException] %1 in %2:%3";
|
||||||
auto message = QString(SCRIPT_EXCEPTION_FORMAT).arg(exception, fileName, line);
|
//auto message = QString(SCRIPT_EXCEPTION_FORMAT).arg(exception, fileName, line);
|
||||||
if (!backtrace.empty()) {
|
//if (!backtrace.empty()) {
|
||||||
static const auto lineSeparator = "\n ";
|
// static const auto lineSeparator = "\n ";
|
||||||
message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator));
|
// message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator));
|
||||||
}
|
//}
|
||||||
qCritical() << qPrintable(message);
|
//qCritical() << qPrintable(message);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -273,7 +274,7 @@ void EntityEditFilters::scriptRequestFinished(EntityItemID entityID) {
|
||||||
FilterData filterData;
|
FilterData filterData;
|
||||||
filterData.engine = engine;
|
filterData.engine = engine;
|
||||||
filterData.rejectAll = false;
|
filterData.rejectAll = false;
|
||||||
|
|
||||||
// define the uncaughtException function
|
// define the uncaughtException function
|
||||||
ScriptEngine& engineRef = *engine;
|
ScriptEngine& engineRef = *engine;
|
||||||
filterData.uncaughtExceptions = [&engineRef, urlString]() { return hadUncaughtExceptions(engineRef, urlString); };
|
filterData.uncaughtExceptions = [&engineRef, urlString]() { return hadUncaughtExceptions(engineRef, urlString); };
|
||||||
|
@ -377,11 +378,11 @@ void EntityEditFilters::scriptRequestFinished(EntityItemID entityID) {
|
||||||
_lock.unlock();
|
_lock.unlock();
|
||||||
|
|
||||||
qDebug() << "script request filter processed for entity id " << entityID;
|
qDebug() << "script request filter processed for entity id " << entityID;
|
||||||
|
|
||||||
emit filterAdded(entityID, true);
|
emit filterAdded(entityID, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (scriptRequest) {
|
} else if (scriptRequest) {
|
||||||
const QString urlString = scriptRequest->getUrl().toString();
|
const QString urlString = scriptRequest->getUrl().toString();
|
||||||
qCritical() << "Failed to download script";
|
qCritical() << "Failed to download script";
|
||||||
|
|
|
@ -285,7 +285,12 @@ glm::uint32 scriptable::ScriptableMesh::forEachVertex(const ScriptValue& _callba
|
||||||
buffer_helpers::mesh::forEachVertex(mesh, [&](glm::uint32 index, const QVariantMap& values) {
|
buffer_helpers::mesh::forEachVertex(mesh, [&](glm::uint32 index, const QVariantMap& values) {
|
||||||
auto result = callback.call(scope, { js->toScriptValue(values), js->newValue(index), meshPart });
|
auto result = callback.call(scope, { js->toScriptValue(values), js->newValue(index), meshPart });
|
||||||
if (js->hasUncaughtException()) {
|
if (js->hasUncaughtException()) {
|
||||||
js->currentContext()->throwValue(js->uncaughtException());
|
// TODO: I think the idea here is that toScriptValue or toValue might throw an exception
|
||||||
|
// in the script engine, which we're then propagating to the running script. This got broken
|
||||||
|
// by the scripting engine changes, needs fixing.
|
||||||
|
|
||||||
|
qCCritical(graphics_scripting) << "Uncaught exception, handling broken, FIX ME: " << js->uncaughtException();
|
||||||
|
//js->currentContext()->throwValue(js->uncaughtException());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
numProcessed++;
|
numProcessed++;
|
||||||
|
@ -295,13 +300,13 @@ glm::uint32 scriptable::ScriptableMesh::forEachVertex(const ScriptValue& _callba
|
||||||
}
|
}
|
||||||
|
|
||||||
/*@jsdoc
|
/*@jsdoc
|
||||||
* Called for each vertex when {@link GraphicsMesh.updateVertexAttributes} is called. The value returned by the script function
|
* Called for each vertex when {@link GraphicsMesh.updateVertexAttributes} is called. The value returned by the script function
|
||||||
* should be the modified attributes to update the vertex with, or <code>false</code> to not update the particular vertex.
|
* should be the modified attributes to update the vertex with, or <code>false</code> to not update the particular vertex.
|
||||||
* @callback GraphicsMesh~updateVertexAttributesCallback
|
* @callback GraphicsMesh~updateVertexAttributesCallback
|
||||||
* @param {Object<Graphics.BufferTypeName, Graphics.BufferType>} attributes - The attributes of the vertex.
|
* @param {Object<Graphics.BufferTypeName, Graphics.BufferType>} attributes - The attributes of the vertex.
|
||||||
* @param {number} index - The vertex index.
|
* @param {number} index - The vertex index.
|
||||||
* @param {object} properties - The properties of the mesh, per {@link GraphicsMesh}.
|
* @param {object} properties - The properties of the mesh, per {@link GraphicsMesh}.
|
||||||
* @returns {Object<Graphics.BufferTypeName, Graphics.BufferType>|boolean} The attribute values to update the vertex with, or
|
* @returns {Object<Graphics.BufferTypeName, Graphics.BufferType>|boolean} The attribute values to update the vertex with, or
|
||||||
* <code>false</code> to not update the vertex.
|
* <code>false</code> to not update the vertex.
|
||||||
*/
|
*/
|
||||||
glm::uint32 scriptable::ScriptableMesh::updateVertexAttributes(const ScriptValue& _callback) {
|
glm::uint32 scriptable::ScriptableMesh::updateVertexAttributes(const ScriptValue& _callback) {
|
||||||
|
@ -325,7 +330,8 @@ glm::uint32 scriptable::ScriptableMesh::updateVertexAttributes(const ScriptValue
|
||||||
auto obj = js->toScriptValue(values);
|
auto obj = js->toScriptValue(values);
|
||||||
auto result = callback.call(scope, { obj, js->newValue(index), meshPart });
|
auto result = callback.call(scope, { obj, js->newValue(index), meshPart });
|
||||||
if (js->hasUncaughtException()) {
|
if (js->hasUncaughtException()) {
|
||||||
js->currentContext()->throwValue(js->uncaughtException());
|
qCCritical(graphics_scripting) << "Uncaught exception, handling broken, FIX ME: " << js->uncaughtException();
|
||||||
|
//js->currentContext()->throwValue(js->uncaughtException());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (result.isBool() && !result.toBool()) {
|
if (result.isBool() && !result.toBool()) {
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
|
|
||||||
#include "ScriptValue.h"
|
#include "ScriptValue.h"
|
||||||
|
#include "ScriptException.h"
|
||||||
|
|
||||||
class QByteArray;
|
class QByteArray;
|
||||||
class QLatin1String;
|
class QLatin1String;
|
||||||
|
@ -55,9 +56,28 @@ inline T scriptvalue_cast(const ScriptValue& value);
|
||||||
* provide the full environment needed to execute scripts.
|
* provide the full environment needed to execute scripts.
|
||||||
*
|
*
|
||||||
* To execute scripts that have access to the API, use ScriptManager.
|
* 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 {
|
class ScriptEngine {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
ScriptEngine(ScriptManager *manager = nullptr) : _manager(manager) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
typedef ScriptValue (*FunctionSignature)(ScriptContext*, ScriptEngine*);
|
typedef ScriptValue (*FunctionSignature)(ScriptContext*, ScriptEngine*);
|
||||||
typedef ScriptValue (*MarshalFunction)(ScriptEngine*, const void*);
|
typedef ScriptValue (*MarshalFunction)(ScriptEngine*, const void*);
|
||||||
typedef bool (*DemarshalFunction)(const ScriptValue&, QVariant &dest);
|
typedef bool (*DemarshalFunction)(const ScriptValue&, QVariant &dest);
|
||||||
|
@ -80,7 +100,9 @@ public:
|
||||||
ScriptOwnership = 1,
|
ScriptOwnership = 1,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Ownership is determined automatically
|
* @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,
|
AutoOwnership = 2,
|
||||||
|
@ -266,7 +288,20 @@ public:
|
||||||
*
|
*
|
||||||
* @return ScriptManager* ScriptManager
|
* @return ScriptManager* ScriptManager
|
||||||
*/
|
*/
|
||||||
virtual ScriptManager* manager() const = 0;
|
ScriptManager* manager() const { return _manager; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Emit the current uncaught exception if there is one
|
||||||
|
*
|
||||||
|
* If there's an uncaught exception, emit it, and clear the exception status.
|
||||||
|
*
|
||||||
|
* This fails if there's no uncaught exception, there's no ScriptManager,
|
||||||
|
* or the engine is evaluating.
|
||||||
|
*
|
||||||
|
* @param debugHint A debugging hint to be added to the error message
|
||||||
|
* @return true There was an uncaught exception, and it was emitted
|
||||||
|
* @return false There was no uncaught exception
|
||||||
|
*/
|
||||||
virtual bool maybeEmitUncaughtException(const QString& debugHint = QString()) = 0;
|
virtual bool maybeEmitUncaughtException(const QString& debugHint = QString()) = 0;
|
||||||
virtual ScriptValue newArray(uint length = 0) = 0;
|
virtual ScriptValue newArray(uint length = 0) = 0;
|
||||||
virtual ScriptValue newArrayBuffer(const QByteArray& message) = 0;
|
virtual ScriptValue newArrayBuffer(const QByteArray& message) = 0;
|
||||||
|
@ -291,9 +326,9 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief Causes an exception to be raised in the currently executing script
|
* @brief Causes an exception to be raised in the currently executing script
|
||||||
*
|
*
|
||||||
* @param exception
|
* @param exception Exception to be thrown in the script
|
||||||
* @return true
|
* @return true Exception was successfully thrown
|
||||||
* @return false
|
* @return false Exception couldn't be thrown because no script is running
|
||||||
*/
|
*/
|
||||||
virtual bool raiseException(const ScriptValue& exception) = 0;
|
virtual bool raiseException(const ScriptValue& exception) = 0;
|
||||||
virtual void registerEnum(const QString& enumName, QMetaEnum newEnum) = 0;
|
virtual void registerEnum(const QString& enumName, QMetaEnum newEnum) = 0;
|
||||||
|
@ -309,14 +344,36 @@ public:
|
||||||
virtual void setThread(QThread* thread) = 0;
|
virtual void setThread(QThread* thread) = 0;
|
||||||
//Q_INVOKABLE virtual void enterIsolateOnThisThread() = 0;
|
//Q_INVOKABLE virtual void enterIsolateOnThisThread() = 0;
|
||||||
virtual ScriptValue undefinedValue() = 0;
|
virtual ScriptValue undefinedValue() = 0;
|
||||||
virtual ScriptValue uncaughtException() const = 0;
|
|
||||||
virtual QStringList uncaughtExceptionBacktrace() const = 0;
|
/**
|
||||||
virtual int uncaughtExceptionLineNumber() const = 0;
|
* @brief Last uncaught exception, if any
|
||||||
|
*
|
||||||
|
* @return ScriptValue Uncaught exception from the script
|
||||||
|
*/
|
||||||
|
virtual ScriptException uncaughtException() const = 0;
|
||||||
|
|
||||||
virtual void updateMemoryCost(const qint64& deltaSize) = 0;
|
virtual void updateMemoryCost(const qint64& deltaSize) = 0;
|
||||||
virtual void requestCollectGarbage() = 0;
|
virtual void requestCollectGarbage() = 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 void compileTest() = 0;
|
||||||
virtual QString scriptValueDebugDetails(const ScriptValue &value) = 0;
|
virtual QString scriptValueDebugDetails(const ScriptValue &value) = 0;
|
||||||
virtual QString scriptValueDebugListMembers(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;
|
virtual void logBacktrace(const QString &title) = 0;
|
||||||
public:
|
public:
|
||||||
// helper to detect and log warnings when other code invokes QScriptEngine/BaseScriptEngine in thread-unsafe ways
|
// helper to detect and log warnings when other code invokes QScriptEngine/BaseScriptEngine in thread-unsafe ways
|
||||||
|
@ -339,8 +396,19 @@ public: // not for public use, but I don't like how Qt strings this along with p
|
||||||
virtual void registerCustomType(int type, MarshalFunction mf, DemarshalFunction df) = 0;
|
virtual void registerCustomType(int type, MarshalFunction mf, DemarshalFunction df) = 0;
|
||||||
virtual QStringList getCurrentScriptURLs() const = 0;
|
virtual QStringList getCurrentScriptURLs() const = 0;
|
||||||
|
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/**
|
||||||
|
* @brief The script being run threw an exception
|
||||||
|
*
|
||||||
|
* @param exception Exception that was thrown
|
||||||
|
*/
|
||||||
|
void exception(const ScriptException &exception);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
~ScriptEngine() {} // prevent explicit deletion of base class
|
~ScriptEngine() {} // prevent explicit deletion of base class
|
||||||
|
|
||||||
|
ScriptManager *_manager;
|
||||||
};
|
};
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(ScriptEngine::QObjectWrapOptions);
|
Q_DECLARE_OPERATORS_FOR_FLAGS(ScriptEngine::QObjectWrapOptions);
|
||||||
|
|
||||||
|
|
80
libraries/script-engine/src/ScriptException.h
Normal file
80
libraries/script-engine/src/ScriptException.h
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
//
|
||||||
|
// ScriptException.h
|
||||||
|
// libraries/script-engine/src
|
||||||
|
//
|
||||||
|
// Created by Dale Glass on 27/02/2023.
|
||||||
|
// 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
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Scripting exception
|
||||||
|
*
|
||||||
|
* Emitted from the scripting engine when an exception happens inside it.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class ScriptException {
|
||||||
|
public:
|
||||||
|
|
||||||
|
ScriptException(QString message = "", QString info = "", int line = 0, int column = 0, QStringList backtraceList = QStringList()) :
|
||||||
|
errorMessage(message), additionalInfo(info), errorLine(line), errorColumn(column), backtrace(backtraceList) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Error message
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
QString errorMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Additional information about the exception
|
||||||
|
*
|
||||||
|
* This is additional information added at the place where the exception happened.
|
||||||
|
* It may contain information about what the system was doing when the exception happened.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
QString additionalInfo;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Error line
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int errorLine;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Error column
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int errorColumn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Backtrace
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
QStringList backtrace;
|
||||||
|
|
||||||
|
bool isEmpty() const { return errorMessage.isEmpty(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
inline QDebug operator<<(QDebug debug, const ScriptException& e) {
|
||||||
|
debug << "Exception:"
|
||||||
|
<< e.errorMessage
|
||||||
|
<< (e.additionalInfo.isEmpty() ? QString("") : "[" + e.additionalInfo + "]")
|
||||||
|
<< " at line " << e.errorLine << ", column " << e.errorColumn;
|
||||||
|
|
||||||
|
if (e.backtrace.length()) {
|
||||||
|
debug << "Backtrace:";
|
||||||
|
debug << e.backtrace;
|
||||||
|
}
|
||||||
|
|
||||||
|
return debug;
|
||||||
|
}
|
|
@ -284,10 +284,16 @@ ScriptManager::ScriptManager(Context context, const QString& scriptContents, con
|
||||||
|
|
||||||
// this is where all unhandled exceptions end up getting logged
|
// this is where all unhandled exceptions end up getting logged
|
||||||
connect(this, &ScriptManager::unhandledException, this, [this](const ScriptValue& err) {
|
connect(this, &ScriptManager::unhandledException, this, [this](const ScriptValue& err) {
|
||||||
|
qCCritical(scriptengine) << "Caught exception";
|
||||||
|
|
||||||
auto output = err.engine() == _engine ? err : _engine->makeError(err);
|
auto output = err.engine() == _engine ? err : _engine->makeError(err);
|
||||||
if (!output.property("detail").isValid()) {
|
if (!output.property("detail").isValid()) {
|
||||||
output.setProperty("detail", "UnhandledException");
|
output.setProperty("detail", "UnhandledException");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unhandled exception kills the running script
|
||||||
|
stop(false);
|
||||||
|
|
||||||
logException(output);
|
logException(output);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -886,6 +892,14 @@ void ScriptManager::run() {
|
||||||
PROFILE_RANGE(script, _fileNameString);
|
PROFILE_RANGE(script, _fileNameString);
|
||||||
_returnValue = _engine->evaluate(_scriptContents, _fileNameString);
|
_returnValue = _engine->evaluate(_scriptContents, _fileNameString);
|
||||||
_engine->maybeEmitUncaughtException(__FUNCTION__);
|
_engine->maybeEmitUncaughtException(__FUNCTION__);
|
||||||
|
|
||||||
|
if (_engine->hasUncaughtException()) {
|
||||||
|
|
||||||
|
qCWarning(scriptengine) << "Engine has uncaught exception, stopping";
|
||||||
|
stop();
|
||||||
|
emit unhandledException(_engine->cloneUncaughtException(__FUNCTION__));
|
||||||
|
_engine->clearExceptions();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// VS13 does not sleep_until unless it uses the system_clock, see:
|
// VS13 does not sleep_until unless it uses the system_clock, see:
|
||||||
|
@ -1008,7 +1022,7 @@ void ScriptManager::run() {
|
||||||
// only clear exceptions if we are not in the middle of evaluating
|
// only clear exceptions if we are not in the middle of evaluating
|
||||||
if (!_engine->isEvaluating() && _engine->hasUncaughtException()) {
|
if (!_engine->isEvaluating() && _engine->hasUncaughtException()) {
|
||||||
qCWarning(scriptengine) << __FUNCTION__ << "---------- UNCAUGHT EXCEPTION --------";
|
qCWarning(scriptengine) << __FUNCTION__ << "---------- UNCAUGHT EXCEPTION --------";
|
||||||
qCWarning(scriptengine) << "runInThread" << _engine->uncaughtException().toString();
|
qCWarning(scriptengine) << "runInThread" << _engine->uncaughtException();
|
||||||
emit unhandledException(_engine->cloneUncaughtException(__FUNCTION__));
|
emit unhandledException(_engine->cloneUncaughtException(__FUNCTION__));
|
||||||
_engine->clearExceptions();
|
_engine->clearExceptions();
|
||||||
}
|
}
|
||||||
|
|
|
@ -266,7 +266,6 @@ public:
|
||||||
* qInfo() << "Done!"
|
* qInfo() << "Done!"
|
||||||
* @endcode
|
* @endcode
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* @note
|
* @note
|
||||||
* Technically, the ScriptManager isn't generic enough -- it implements things that imitate
|
* Technically, the ScriptManager isn't generic enough -- it implements things that imitate
|
||||||
* Node.js for examine in the module loading code, which makes it JS specific. This code
|
* Node.js for examine in the module loading code, which makes it JS specific. This code
|
||||||
|
|
|
@ -291,14 +291,18 @@ bool ScriptEngineV8::raiseException(const V8ScriptValue& exception) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScriptEngineV8::maybeEmitUncaughtException(const QString& debugHint) {
|
bool ScriptEngineV8::maybeEmitUncaughtException(const QString& debugHint) {
|
||||||
|
|
||||||
|
/*
|
||||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!isEvaluating() && hasUncaughtException() && _scriptManager) {
|
if (!isEvaluating() && hasUncaughtException()) {
|
||||||
emit _scriptManager->unhandledException(cloneUncaughtException(debugHint));
|
emit exception(cloneUncaughtException(debugHint));
|
||||||
clearExceptions();
|
clearExceptions();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,8 +392,7 @@ v8::Platform* ScriptEngineV8::getV8Platform() {
|
||||||
return platform.get();
|
return platform.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptEngineV8::ScriptEngineV8(ScriptManager* scriptManager) :
|
ScriptEngineV8::ScriptEngineV8(ScriptManager *manager) : ScriptEngine(manager), _evaluatingCounter(0)
|
||||||
_scriptManager(scriptManager), _evaluatingCounter(0)
|
|
||||||
//V8TODO
|
//V8TODO
|
||||||
//_arrayBufferClass(new ArrayBufferClass(this))
|
//_arrayBufferClass(new ArrayBufferClass(this))
|
||||||
{
|
{
|
||||||
|
@ -451,7 +454,7 @@ ScriptEngineV8::ScriptEngineV8(ScriptManager* scriptManager) :
|
||||||
|
|
||||||
//_currentThread = QThread::currentThread();
|
//_currentThread = QThread::currentThread();
|
||||||
|
|
||||||
if (_scriptManager) {
|
//if (_scriptManager) {
|
||||||
// V8TODO: port to V8
|
// V8TODO: port to V8
|
||||||
/*connect(this, &QScriptEngine::signalHandlerException, this, [this](const V8ScriptValue& exception) {
|
/*connect(this, &QScriptEngine::signalHandlerException, this, [this](const V8ScriptValue& exception) {
|
||||||
if (hasUncaughtException()) {
|
if (hasUncaughtException()) {
|
||||||
|
@ -466,7 +469,7 @@ ScriptEngineV8::ScriptEngineV8(ScriptManager* scriptManager) :
|
||||||
}, Qt::DirectConnection);*/
|
}, Qt::DirectConnection);*/
|
||||||
//moveToThread(scriptManager->thread());
|
//moveToThread(scriptManager->thread());
|
||||||
//setThread(scriptManager->thread());
|
//setThread(scriptManager->thread());
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEngineV8::registerEnum(const QString& enumName, QMetaEnum newEnum) {
|
void ScriptEngineV8::registerEnum(const QString& enumName, QMetaEnum newEnum) {
|
||||||
|
@ -1044,10 +1047,6 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure,
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptValue ScriptEngineV8::evaluate(const QString& sourceCode, const QString& fileName) {
|
ScriptValue ScriptEngineV8::evaluate(const QString& sourceCode, const QString& fileName) {
|
||||||
if (_scriptManager && _scriptManager->isStopped()) {
|
|
||||||
return undefinedValue(); // bail early
|
|
||||||
}
|
|
||||||
|
|
||||||
//V8TODO
|
//V8TODO
|
||||||
|
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
|
@ -1132,8 +1131,12 @@ ScriptValue ScriptEngineV8::evaluate(const QString& sourceCode, const QString& f
|
||||||
ScriptValue errorValue(new ScriptValueV8Wrapper(this, V8ScriptValue(this, runError->Get())));
|
ScriptValue errorValue(new ScriptValueV8Wrapper(this, V8ScriptValue(this, runError->Get())));
|
||||||
qCDebug(scriptengine) << "Running script: \"" << fileName << "\" " << formatErrorMessageFromTryCatch(tryCatchRun);
|
qCDebug(scriptengine) << "Running script: \"" << fileName << "\" " << formatErrorMessageFromTryCatch(tryCatchRun);
|
||||||
//V8TODO
|
//V8TODO
|
||||||
|
|
||||||
|
|
||||||
//raiseException(errorValue);
|
//raiseException(errorValue);
|
||||||
//maybeEmitUncaughtException("evaluate");
|
//maybeEmitUncaughtException("evaluate");
|
||||||
|
setUncaughtException(tryCatchRun, "script evaluation");
|
||||||
|
|
||||||
_evaluatingCounter--;
|
_evaluatingCounter--;
|
||||||
return errorValue;
|
return errorValue;
|
||||||
}
|
}
|
||||||
|
@ -1142,6 +1145,53 @@ ScriptValue ScriptEngineV8::evaluate(const QString& sourceCode, const QString& f
|
||||||
return ScriptValue(new ScriptValueV8Wrapper(this, std::move(resultValue)));
|
return ScriptValue(new ScriptValueV8Wrapper(this, std::move(resultValue)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScriptEngineV8::setUncaughtException(const v8::TryCatch &tryCatch, const QString& info) {
|
||||||
|
if (!tryCatch.HasCaught()) {
|
||||||
|
qCWarning(scriptengine) << "setUncaughtException called without exception";
|
||||||
|
clearExceptions();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptException ex;
|
||||||
|
ex.additionalInfo = info;
|
||||||
|
|
||||||
|
v8::Locker locker(_v8Isolate);
|
||||||
|
v8::Isolate::Scope isolateScope(_v8Isolate);
|
||||||
|
v8::HandleScope handleScope(_v8Isolate);
|
||||||
|
v8::Context::Scope contextScope(getContext());
|
||||||
|
QString result("");
|
||||||
|
|
||||||
|
QString errorMessage = "";
|
||||||
|
QString errorBacktrace = "";
|
||||||
|
//v8::String::Utf8Value utf8Value(getIsolate(), tryCatch.Exception());
|
||||||
|
v8::String::Utf8Value utf8Value(getIsolate(), tryCatch.Message()->Get());
|
||||||
|
|
||||||
|
ex.errorMessage = QString(*utf8Value);
|
||||||
|
|
||||||
|
v8::Local<v8::Message> exceptionMessage = tryCatch.Message();
|
||||||
|
if (!exceptionMessage.IsEmpty()) {
|
||||||
|
ex.errorLine = exceptionMessage->GetLineNumber(getContext()).FromJust();
|
||||||
|
ex.errorColumn = exceptionMessage->GetStartColumn(getContext()).FromJust();
|
||||||
|
v8::Local<v8::Value> backtraceV8String;
|
||||||
|
if (tryCatch.StackTrace(getContext()).ToLocal(&backtraceV8String)) {
|
||||||
|
if (backtraceV8String->IsString()) {
|
||||||
|
if (v8::Local<v8::String>::Cast(backtraceV8String)->Length() > 0) {
|
||||||
|
v8::String::Utf8Value backtraceUtf8Value(getIsolate(), backtraceV8String);
|
||||||
|
QString errorBacktrace = *backtraceUtf8Value;
|
||||||
|
ex.backtrace = errorBacktrace.split("\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(scriptengine) << "Emitting exception:" << ex;
|
||||||
|
_uncaughtException = ex;
|
||||||
|
emit exception(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
QString ScriptEngineV8::formatErrorMessageFromTryCatch(v8::TryCatch &tryCatch) {
|
QString ScriptEngineV8::formatErrorMessageFromTryCatch(v8::TryCatch &tryCatch) {
|
||||||
v8::Locker locker(_v8Isolate);
|
v8::Locker locker(_v8Isolate);
|
||||||
v8::Isolate::Scope isolateScope(_v8Isolate);
|
v8::Isolate::Scope isolateScope(_v8Isolate);
|
||||||
|
@ -1194,9 +1244,6 @@ void ScriptEngineV8::popContext() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_INVOKABLE ScriptValue ScriptEngineV8::evaluate(const ScriptProgramPointer& program) {
|
Q_INVOKABLE ScriptValue ScriptEngineV8::evaluate(const ScriptProgramPointer& program) {
|
||||||
if (_scriptManager && _scriptManager->isStopped()) {
|
|
||||||
return undefinedValue(); // bail early
|
|
||||||
}
|
|
||||||
|
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
ScriptValue result;
|
ScriptValue result;
|
||||||
|
@ -1308,10 +1355,6 @@ ScriptValue ScriptEngineV8::globalObject() {
|
||||||
return ScriptValue(new ScriptValueV8Wrapper(const_cast<ScriptEngineV8*>(this), std::move(global)));
|
return ScriptValue(new ScriptValueV8Wrapper(const_cast<ScriptEngineV8*>(this), std::move(global)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptManager* ScriptEngineV8::manager() const {
|
|
||||||
return _scriptManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScriptValue ScriptEngineV8::newArray(uint length) {
|
ScriptValue ScriptEngineV8::newArray(uint length) {
|
||||||
v8::Locker locker(_v8Isolate);
|
v8::Locker locker(_v8Isolate);
|
||||||
v8::Isolate::Scope isolateScope(_v8Isolate);
|
v8::Isolate::Scope isolateScope(_v8Isolate);
|
||||||
|
@ -1486,8 +1529,7 @@ void ScriptEngineV8::abortEvaluation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEngineV8::clearExceptions() {
|
void ScriptEngineV8::clearExceptions() {
|
||||||
//V8TODO
|
_uncaughtException = ScriptException();
|
||||||
//QScriptEngine::clearExceptions();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptContext* ScriptEngineV8::currentContext() const {
|
ScriptContext* ScriptEngineV8::currentContext() const {
|
||||||
|
@ -1509,9 +1551,7 @@ ScriptContext* ScriptEngineV8::currentContext() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScriptEngineV8::hasUncaughtException() const {
|
bool ScriptEngineV8::hasUncaughtException() const {
|
||||||
//V8TODO
|
return !_uncaughtException.isEmpty();
|
||||||
//return QScriptEngine::hasUncaughtException();
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScriptEngineV8::isEvaluating() const {
|
bool ScriptEngineV8::isEvaluating() const {
|
||||||
|
@ -1627,32 +1667,20 @@ void ScriptEngineV8::setThread(QThread* thread) {
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
|
||||||
ScriptValue ScriptEngineV8::uncaughtException() const {
|
ScriptException ScriptEngineV8::uncaughtException() const {
|
||||||
//V8TODO
|
return _uncaughtException;
|
||||||
//V8ScriptValue result = QScriptEngine::uncaughtException();
|
|
||||||
//return ScriptValue(new ScriptValueV8Wrapper(const_cast<ScriptEngineV8*>(this), std::move(result)));
|
|
||||||
return ScriptValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList ScriptEngineV8::uncaughtExceptionBacktrace() const {
|
|
||||||
//V8TODO
|
|
||||||
//return QScriptEngine::uncaughtExceptionBacktrace();
|
|
||||||
return QStringList();
|
|
||||||
}
|
|
||||||
|
|
||||||
int ScriptEngineV8::uncaughtExceptionLineNumber() const {
|
|
||||||
//V8TODO
|
|
||||||
//return QScriptEngine::uncaughtExceptionLineNumber();
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScriptEngineV8::raiseException(const ScriptValue& exception) {
|
bool ScriptEngineV8::raiseException(const ScriptValue& exception) {
|
||||||
//V8TODO
|
//V8TODO
|
||||||
//Q_ASSERT(false);
|
//Q_ASSERT(false);
|
||||||
qCritical() << "Script exception occurred: " << exception.toString();
|
// qCritical() << "Script exception occurred: " << exception.toString();
|
||||||
/*ScriptValueV8Wrapper* unwrapped = ScriptValueV8Wrapper::unwrap(exception);
|
// ScriptValueV8Wrapper* unwrapped = ScriptValueV8Wrapper::unwrap(exception);
|
||||||
V8ScriptValue qException = unwrapped ? unwrapped->toV8Value() : QScriptEngine::newVariant(exception.toVariant());
|
// V8ScriptValue qException = unwrapped ? unwrapped->toV8Value() : QScriptEngine::newVariant(exception.toVariant());
|
||||||
return raiseException(qException);*/
|
|
||||||
|
// emit
|
||||||
|
//return raiseException(qException);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
#include "../ScriptEngine.h"
|
#include "../ScriptEngine.h"
|
||||||
#include "../ScriptManager.h"
|
#include "../ScriptManager.h"
|
||||||
|
#include "../ScriptException.h"
|
||||||
//#include "V8Types.h"
|
//#include "V8Types.h"
|
||||||
|
|
||||||
#include "ArrayBufferClass.h"
|
#include "ArrayBufferClass.h"
|
||||||
|
@ -61,7 +62,7 @@ class ScriptEngineV8 final : public QObject, public ScriptEngine,
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public: // construction
|
public: // construction
|
||||||
ScriptEngineV8(ScriptManager* scriptManager = nullptr);
|
ScriptEngineV8(ScriptManager *manager = nullptr);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// NOTE - these are NOT intended to be public interfaces available to scripts, the are only Q_INVOKABLE so we can
|
// NOTE - these are NOT intended to be public interfaces available to scripts, the are only Q_INVOKABLE so we can
|
||||||
|
@ -81,7 +82,7 @@ public: // ScriptEngine implementation
|
||||||
//virtual ScriptValue lintScript(const QString& sourceCode, const QString& fileName, const int lineNumber = 1) override;
|
//virtual ScriptValue lintScript(const QString& sourceCode, const QString& fileName, const int lineNumber = 1) override;
|
||||||
virtual ScriptValue checkScriptSyntax(ScriptProgramPointer program) override;
|
virtual ScriptValue checkScriptSyntax(ScriptProgramPointer program) override;
|
||||||
virtual ScriptValue makeError(const ScriptValue& other, const QString& type = "Error") override;
|
virtual ScriptValue makeError(const ScriptValue& other, const QString& type = "Error") override;
|
||||||
virtual ScriptManager* manager() const override;
|
|
||||||
|
|
||||||
// if there is a pending exception and we are at the top level (non-recursive) stack frame, this emits and resets it
|
// if there is a pending exception and we are at the top level (non-recursive) stack frame, this emits and resets it
|
||||||
virtual bool maybeEmitUncaughtException(const QString& debugHint = QString()) override;
|
virtual bool maybeEmitUncaughtException(const QString& debugHint = QString()) override;
|
||||||
|
@ -127,9 +128,7 @@ public: // ScriptEngine implementation
|
||||||
virtual void setThread(QThread* thread) override;
|
virtual void setThread(QThread* thread) override;
|
||||||
//Q_INVOKABLE virtual void enterIsolateOnThisThread() override;
|
//Q_INVOKABLE virtual void enterIsolateOnThisThread() override;
|
||||||
virtual ScriptValue undefinedValue() override;
|
virtual ScriptValue undefinedValue() override;
|
||||||
virtual ScriptValue uncaughtException() const override;
|
virtual ScriptException uncaughtException() const override;
|
||||||
virtual QStringList uncaughtExceptionBacktrace() const override;
|
|
||||||
virtual int uncaughtExceptionLineNumber() const override;
|
|
||||||
virtual void updateMemoryCost(const qint64& deltaSize) override;
|
virtual void updateMemoryCost(const qint64& deltaSize) override;
|
||||||
virtual void requestCollectGarbage() override { while(!_v8Isolate->IdleNotificationDeadline(getV8Platform()->MonotonicallyIncreasingTime() + GARBAGE_COLLECTION_TIME_LIMIT_S)) {}; }
|
virtual void requestCollectGarbage() override { while(!_v8Isolate->IdleNotificationDeadline(getV8Platform()->MonotonicallyIncreasingTime() + GARBAGE_COLLECTION_TIME_LIMIT_S)) {}; }
|
||||||
virtual void compileTest() override;
|
virtual void compileTest() override;
|
||||||
|
@ -218,16 +217,28 @@ protected:
|
||||||
const ValueOwnership& ownership = AutoOwnership);*/
|
const ValueOwnership& ownership = AutoOwnership);*/
|
||||||
|
|
||||||
void registerSystemTypes();
|
void registerSystemTypes();
|
||||||
|
signals:
|
||||||
|
/**
|
||||||
|
* @brief The script being run threw an exception
|
||||||
|
*
|
||||||
|
* @param exception Exception that was thrown
|
||||||
|
*/
|
||||||
|
void exception(const ScriptException &exception);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static QMutex _v8InitMutex;
|
static QMutex _v8InitMutex;
|
||||||
static std::once_flag _v8InitOnceFlag;
|
static std::once_flag _v8InitOnceFlag;
|
||||||
static v8::Platform* getV8Platform();
|
static v8::Platform* getV8Platform();
|
||||||
|
|
||||||
|
void setUncaughtException(const v8::TryCatch &tryCatch, const QString& info = QString());
|
||||||
|
|
||||||
|
ScriptException _uncaughtException;
|
||||||
|
|
||||||
|
|
||||||
// V8TODO: clean up isolate when script engine is destroyed?
|
// V8TODO: clean up isolate when script engine is destroyed?
|
||||||
v8::Isolate* _v8Isolate;
|
v8::Isolate* _v8Isolate;
|
||||||
//v8::UniquePersistent<v8::Context> _v8Context;
|
//v8::UniquePersistent<v8::Context> _v8Context;
|
||||||
|
|
||||||
struct CustomMarshal {
|
struct CustomMarshal {
|
||||||
ScriptEngine::MarshalFunction marshalFunc;
|
ScriptEngine::MarshalFunction marshalFunc;
|
||||||
ScriptEngine::DemarshalFunction demarshalFunc;
|
ScriptEngine::DemarshalFunction demarshalFunc;
|
||||||
|
@ -235,8 +246,6 @@ protected:
|
||||||
using CustomMarshalMap = QHash<int, CustomMarshal>;
|
using CustomMarshalMap = QHash<int, CustomMarshal>;
|
||||||
using CustomPrototypeMap = QHash<int, V8ScriptValue>;
|
using CustomPrototypeMap = QHash<int, V8ScriptValue>;
|
||||||
|
|
||||||
QPointer<ScriptManager> _scriptManager;
|
|
||||||
|
|
||||||
mutable QReadWriteLock _customTypeProtect { QReadWriteLock::Recursive };
|
mutable QReadWriteLock _customTypeProtect { QReadWriteLock::Recursive };
|
||||||
CustomMarshalMap _customTypes;
|
CustomMarshalMap _customTypes;
|
||||||
CustomPrototypeMap _customPrototypes;
|
CustomPrototypeMap _customPrototypes;
|
||||||
|
|
Loading…
Reference in a new issue