mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 01:47:31 +02:00
Further exception work on V8
* Get rid of maybeEmitUncaughtException * Mostly get rid of makeError * Introduce exception hierarchy, change exceptions to shared_ptr * Simplify exception throwing code
This commit is contained in:
parent
c0e62c5cc2
commit
67e7a7375a
10 changed files with 249 additions and 260 deletions
|
@ -132,7 +132,7 @@ void EntityScriptingInterface::releaseEntityPacketSenderMessages(bool wait) {
|
||||||
void EntityScriptingInterface::attachDefaultEventHandlers(ScriptManager* manager) {
|
void EntityScriptingInterface::attachDefaultEventHandlers(ScriptManager* manager) {
|
||||||
// Connect up ALL the handlers to the global entities object's signals.
|
// Connect up ALL the handlers to the global entities object's signals.
|
||||||
// (We could go signal by signal, or even handler by handler, but I don't think the efficiency is worth the complexity.)
|
// (We could go signal by signal, or even handler by handler, but I don't think the efficiency is worth the complexity.)
|
||||||
|
|
||||||
// Bug? These handlers are deleted when entityID is deleted, which is nice.
|
// Bug? These handlers are deleted when entityID is deleted, which is nice.
|
||||||
// But if they are created by an entity script on a different entity, should they also be deleted when the entity script unloads?
|
// But if they are created by an entity script on a different entity, should they also be deleted when the entity script unloads?
|
||||||
// E.g., suppose a bow has an entity script that causes arrows to be created with a potential lifetime greater than the bow,
|
// E.g., suppose a bow has an entity script that causes arrows to be created with a potential lifetime greater than the bow,
|
||||||
|
@ -194,7 +194,7 @@ void EntityScriptingInterface::attachDefaultEventHandlers(ScriptManager* manager
|
||||||
};
|
};
|
||||||
|
|
||||||
/*@jsdoc
|
/*@jsdoc
|
||||||
* <p>The name of an entity event. When the entity event occurs, any function that has been registered for that event
|
* <p>The name of an entity event. When the entity event occurs, any function that has been registered for that event
|
||||||
* via {@link Script.addEventHandler} is called with parameters per the entity event.</p>
|
* via {@link Script.addEventHandler} is called with parameters per the entity event.</p>
|
||||||
* <table>
|
* <table>
|
||||||
* <thead>
|
* <thead>
|
||||||
|
@ -782,7 +782,7 @@ QUuid EntityScriptingInterface::cloneEntity(const QUuid& entityIDToClone) {
|
||||||
} else if (cloneAvatarEntity) {
|
} else if (cloneAvatarEntity) {
|
||||||
return addEntityInternal(properties, entity::HostType::AVATAR);
|
return addEntityInternal(properties, entity::HostType::AVATAR);
|
||||||
} else {
|
} else {
|
||||||
// setLastEdited timestamp to 0 to ensure this entity gets updated with the properties
|
// setLastEdited timestamp to 0 to ensure this entity gets updated with the properties
|
||||||
// from the server-created entity, don't change this unless you know what you are doing
|
// from the server-created entity, don't change this unless you know what you are doing
|
||||||
properties.setLastEdited(0);
|
properties.setLastEdited(0);
|
||||||
bool success = addLocalEntityCopy(properties, newEntityID, true);
|
bool success = addLocalEntityCopy(properties, newEntityID, true);
|
||||||
|
@ -1255,7 +1255,7 @@ void EntityScriptingInterface::setNonPersistentEntitiesScriptEngine(std::shared_
|
||||||
|
|
||||||
void EntityScriptingInterface::callEntityMethod(const QUuid& id, const QString& method, const QStringList& params) {
|
void EntityScriptingInterface::callEntityMethod(const QUuid& id, const QString& method, const QStringList& params) {
|
||||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||||
|
|
||||||
auto entity = getEntityTree()->findEntityByEntityItemID(id);
|
auto entity = getEntityTree()->findEntityByEntityItemID(id);
|
||||||
if (entity) {
|
if (entity) {
|
||||||
std::lock_guard<std::recursive_mutex> lock(_entitiesScriptEngineLock);
|
std::lock_guard<std::recursive_mutex> lock(_entitiesScriptEngineLock);
|
||||||
|
@ -1605,7 +1605,7 @@ bool EntityScriptingInterface::queryPropertyMetadata(const QUuid& entityID,
|
||||||
#endif
|
#endif
|
||||||
if (!handler.property("callback").isFunction()) {
|
if (!handler.property("callback").isFunction()) {
|
||||||
qDebug() << "!handler.callback.isFunction" << manager;
|
qDebug() << "!handler.callback.isFunction" << manager;
|
||||||
engine->raiseException(engine->makeError(engine->newValue("callback is not a function"), "TypeError"));
|
engine->raiseException("callback is not a function", "TypeError");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1628,8 +1628,7 @@ bool EntityScriptingInterface::queryPropertyMetadata(const QUuid& entityID,
|
||||||
} else if (name == "serverScripts") {
|
} else if (name == "serverScripts") {
|
||||||
return request.serverScripts(entityID, handler);
|
return request.serverScripts(entityID, handler);
|
||||||
} else {
|
} else {
|
||||||
engine->raiseException(engine->makeError(engine->newValue("metadata for property " + name + " is not yet queryable")));
|
engine->raiseException("metadata for property " + name + " is not yet queryable");
|
||||||
engine->maybeEmitUncaughtException(__FUNCTION__);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1644,8 +1643,7 @@ bool EntityScriptingInterface::getServerScriptStatus(const QUuid& entityID, Scri
|
||||||
Q_ASSERT(QThread::currentThread() == engine->thread());
|
Q_ASSERT(QThread::currentThread() == engine->thread());
|
||||||
Q_ASSERT(QThread::currentThread() == engine->manager()->thread());
|
Q_ASSERT(QThread::currentThread() == engine->manager()->thread());
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
engine->raiseException(engine->makeError(engine->newValue("This script does not belong to a ScriptManager")));
|
engine->raiseException("This script does not belong to a ScriptManager");
|
||||||
engine->maybeEmitUncaughtException(__FUNCTION__);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2015,17 +2013,17 @@ EntityItemPointer EntityScriptingInterface::checkForTreeEntityAndTypeMatch(const
|
||||||
if (!_entityTree) {
|
if (!_entityTree) {
|
||||||
return EntityItemPointer();
|
return EntityItemPointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
|
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
qCDebug(entities) << "EntityScriptingInterface::checkForTreeEntityAndTypeMatch - no entity with ID" << entityID;
|
qCDebug(entities) << "EntityScriptingInterface::checkForTreeEntityAndTypeMatch - no entity with ID" << entityID;
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entityType != EntityTypes::Unknown && entity->getType() != entityType) {
|
if (entityType != EntityTypes::Unknown && entity->getType() != entityType) {
|
||||||
return EntityItemPointer();
|
return EntityItemPointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -179,14 +179,6 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual void clearExceptions() = 0;
|
virtual void clearExceptions() = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Creates a clone of the current exception
|
|
||||||
*
|
|
||||||
* @param detail Additional text to add to the report
|
|
||||||
* @return ScriptValue Result
|
|
||||||
*/
|
|
||||||
virtual ScriptValue cloneUncaughtException(const QString& detail = QString()) = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Context of the currently running script
|
* @brief Context of the currently running script
|
||||||
*
|
*
|
||||||
|
@ -274,15 +266,6 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual ScriptValue checkScriptSyntax(ScriptProgramPointer program) = 0;
|
virtual ScriptValue checkScriptSyntax(ScriptProgramPointer program) = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Creates a ScriptValue that contains an error
|
|
||||||
*
|
|
||||||
* @param other
|
|
||||||
* @param type
|
|
||||||
* @return ScriptValue
|
|
||||||
*/
|
|
||||||
virtual ScriptValue makeError(const ScriptValue& other = ScriptValue(), const QString& type = "Error") = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Pointer to the ScriptManager that controls this scripting engine
|
* @brief Pointer to the ScriptManager that controls this scripting engine
|
||||||
*
|
*
|
||||||
|
@ -290,19 +273,6 @@ public:
|
||||||
*/
|
*/
|
||||||
ScriptManager* manager() const { return _manager; }
|
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 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;
|
||||||
virtual ScriptValue newFunction(FunctionSignature fun, int length = 0) {
|
virtual ScriptValue newFunction(FunctionSignature fun, int length = 0) {
|
||||||
|
@ -323,14 +293,39 @@ public:
|
||||||
virtual ScriptValue nullValue() = 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
|
* @brief Causes an exception to be raised in the currently executing script
|
||||||
*
|
*
|
||||||
* @param exception Exception to be thrown in the 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 true Exception was successfully thrown
|
||||||
* @return false Exception couldn't be thrown because no script is running
|
* @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, 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(const QString& enumName, QMetaEnum newEnum) = 0;
|
virtual void registerEnum(const QString& enumName, QMetaEnum newEnum) = 0;
|
||||||
virtual void registerFunction(const QString& name, FunctionSignature fun, int numArguments = -1) = 0;
|
virtual void registerFunction(const QString& name, FunctionSignature fun, int numArguments = -1) = 0;
|
||||||
virtual void registerFunction(const QString& parent, const QString& name, FunctionSignature fun, int numArguments = -1) = 0;
|
virtual void registerFunction(const QString& parent, const QString& name, FunctionSignature fun, int numArguments = -1) = 0;
|
||||||
|
@ -346,11 +341,14 @@ public:
|
||||||
virtual ScriptValue undefinedValue() = 0;
|
virtual ScriptValue undefinedValue() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Last uncaught exception, if any
|
* @brief Last uncaught exception, if any.
|
||||||
*
|
*
|
||||||
* @return ScriptValue Uncaught exception from the script
|
* 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 ScriptException uncaughtException() const = 0;
|
virtual std::shared_ptr<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;
|
||||||
|
@ -403,7 +401,7 @@ signals:
|
||||||
*
|
*
|
||||||
* @param exception Exception that was thrown
|
* @param exception Exception that was thrown
|
||||||
*/
|
*/
|
||||||
void exception(const ScriptException &exception);
|
void exception(std::shared_ptr<ScriptException> exception);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
~ScriptEngine() {} // prevent explicit deletion of base class
|
~ScriptEngine() {} // prevent explicit deletion of base class
|
||||||
|
|
|
@ -12,12 +12,15 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
|
#include "ScriptValue.h"
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Scripting exception
|
* @brief Scripting exception
|
||||||
*
|
*
|
||||||
* Emitted from the scripting engine when an exception happens inside it.
|
* Emitted from the scripting engine when an exception happens inside it.
|
||||||
|
* This is the base class.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class ScriptException {
|
class ScriptException {
|
||||||
|
@ -63,6 +66,89 @@ class ScriptException {
|
||||||
QStringList backtrace;
|
QStringList backtrace;
|
||||||
|
|
||||||
bool isEmpty() const { return errorMessage.isEmpty(); }
|
bool isEmpty() const { return errorMessage.isEmpty(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clones this object.
|
||||||
|
*
|
||||||
|
* This is used in the scripting engine to ensure that while it can return different
|
||||||
|
* exception objects depending on what happened, the returned object is a copy that
|
||||||
|
* doesn't allow the caller to accidentally break the ScriptEngine's internal state.
|
||||||
|
*
|
||||||
|
* @return std::shared_ptr<ScriptException>
|
||||||
|
*/
|
||||||
|
virtual std::shared_ptr<ScriptException> clone() const {
|
||||||
|
return std::make_shared<ScriptException>(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Exception that ocurred inside the scripting engine on the c++ side
|
||||||
|
*
|
||||||
|
* This is something that went wrong inside the ScriptEngine or ScriptManager
|
||||||
|
* infrastructure.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class ScriptEngineException : public ScriptException {
|
||||||
|
public:
|
||||||
|
|
||||||
|
ScriptEngineException(QString message = "", QString info = "", int line = 0, int column = 0, QStringList backtraceList = QStringList()) :
|
||||||
|
ScriptException(message, info, line, column, backtraceList) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clones this object.
|
||||||
|
*
|
||||||
|
* This is used in the scripting engine to ensure that while it can return different
|
||||||
|
* exception objects depending on what happened, the returned object is a copy that
|
||||||
|
* doesn't allow the caller to accidentally break the ScriptEngine's internal state.
|
||||||
|
*
|
||||||
|
* @return std::shared_ptr<ScriptException>
|
||||||
|
*/
|
||||||
|
virtual std::shared_ptr<ScriptException> clone() const override {
|
||||||
|
return std::make_shared<ScriptEngineException>(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Exception that ocurred inside the running script
|
||||||
|
*
|
||||||
|
* This is something that went wrong inside the running script.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class ScriptRuntimeException : public ScriptException {
|
||||||
|
public:
|
||||||
|
|
||||||
|
ScriptRuntimeException(QString message = "", QString info = "", int line = 0, int column = 0, QStringList backtraceList = QStringList()) :
|
||||||
|
ScriptException(message, info, line, column, backtraceList) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The actual value that was thrown by the script.
|
||||||
|
*
|
||||||
|
* The content is completely arbitrary.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
ScriptValue thrownValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clones this object.
|
||||||
|
*
|
||||||
|
* This is used in the scripting engine to ensure that while it can return different
|
||||||
|
* exception objects depending on what happened, the returned object is a copy that
|
||||||
|
* doesn't allow the caller to accidentally break the ScriptEngine's internal state.
|
||||||
|
*
|
||||||
|
* @return std::shared_ptr<ScriptException>
|
||||||
|
*/
|
||||||
|
virtual std::shared_ptr<ScriptException> clone() const override {
|
||||||
|
return std::make_shared<ScriptRuntimeException>(*this);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline QDebug operator<<(QDebug debug, const ScriptException& e) {
|
inline QDebug operator<<(QDebug debug, const ScriptException& e) {
|
||||||
|
@ -76,5 +162,20 @@ inline QDebug operator<<(QDebug debug, const ScriptException& e) {
|
||||||
debug << e.backtrace;
|
debug << e.backtrace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is this a bad practice?
|
||||||
|
inline QDebug operator<<(QDebug debug, std::shared_ptr<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;
|
return debug;
|
||||||
}
|
}
|
|
@ -282,6 +282,11 @@ ScriptManager::ScriptManager(Context context, const QString& scriptContents, con
|
||||||
processLevelMaxRetries = 1;
|
processLevelMaxRetries = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// V8TODO: Is this actually needed? Exceptions will already be logged on the
|
||||||
|
// script engine side.
|
||||||
|
|
||||||
// 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";
|
qCCritical(scriptengine) << "Caught exception";
|
||||||
|
@ -296,6 +301,7 @@ ScriptManager::ScriptManager(Context context, const QString& scriptContents, con
|
||||||
|
|
||||||
logException(output);
|
logException(output);
|
||||||
});
|
});
|
||||||
|
#endif
|
||||||
|
|
||||||
if (_type == Type::ENTITY_CLIENT || _type == Type::ENTITY_SERVER) {
|
if (_type == Type::ENTITY_CLIENT || _type == Type::ENTITY_SERVER) {
|
||||||
QObject::connect(this, &ScriptManager::update, this, [this]() {
|
QObject::connect(this, &ScriptManager::update, this, [this]() {
|
||||||
|
@ -891,13 +897,11 @@ void ScriptManager::run() {
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(script, _fileNameString);
|
PROFILE_RANGE(script, _fileNameString);
|
||||||
_returnValue = _engine->evaluate(_scriptContents, _fileNameString);
|
_returnValue = _engine->evaluate(_scriptContents, _fileNameString);
|
||||||
_engine->maybeEmitUncaughtException(__FUNCTION__);
|
|
||||||
|
|
||||||
if (_engine->hasUncaughtException()) {
|
if (_engine->hasUncaughtException()) {
|
||||||
|
|
||||||
qCWarning(scriptengine) << "Engine has uncaught exception, stopping";
|
qCWarning(scriptengine) << "Engine has uncaught exception, stopping";
|
||||||
stop();
|
stop();
|
||||||
emit unhandledException(_engine->cloneUncaughtException(__FUNCTION__));
|
|
||||||
_engine->clearExceptions();
|
_engine->clearExceptions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1023,7 +1027,7 @@ void ScriptManager::run() {
|
||||||
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();
|
qCWarning(scriptengine) << "runInThread" << _engine->uncaughtException();
|
||||||
emit unhandledException(_engine->cloneUncaughtException(__FUNCTION__));
|
emit unhandledException(_engine->uncaughtException());
|
||||||
_engine->clearExceptions();
|
_engine->clearExceptions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1258,9 +1262,8 @@ QString ScriptManager::_requireResolve(const QString& moduleId, const QString& r
|
||||||
}
|
}
|
||||||
auto message = QString("Cannot find module '%1' (%2)").arg(displayId);
|
auto message = QString("Cannot find module '%1' (%2)").arg(displayId);
|
||||||
|
|
||||||
auto throwResolveError = [&](const ScriptValue& error) -> QString {
|
auto throwResolveError = [&](const QString& error, const QString &details = QString()) -> QString {
|
||||||
_engine->raiseException(error);
|
_engine->raiseException(error, "require.resolve: " + details);
|
||||||
_engine->maybeEmitUncaughtException("require.resolve");
|
|
||||||
return QString();
|
return QString();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1269,7 +1272,7 @@ QString ScriptManager::_requireResolve(const QString& moduleId, const QString& r
|
||||||
if (idLength < 1 || idLength > MAX_MODULE_ID_LENGTH) {
|
if (idLength < 1 || idLength > MAX_MODULE_ID_LENGTH) {
|
||||||
auto details = QString("rejecting invalid module id size (%1 chars [1,%2])")
|
auto details = QString("rejecting invalid module id size (%1 chars [1,%2])")
|
||||||
.arg(idLength).arg(MAX_MODULE_ID_LENGTH);
|
.arg(idLength).arg(MAX_MODULE_ID_LENGTH);
|
||||||
return throwResolveError(_engine->makeError(_engine->newValue(message.arg(details)), "RangeError"));
|
return throwResolveError(details, "RangeError");
|
||||||
}
|
}
|
||||||
|
|
||||||
// this regex matches: absolute, dotted or path-like URLs
|
// this regex matches: absolute, dotted or path-like URLs
|
||||||
|
@ -1296,15 +1299,15 @@ QString ScriptManager::_requireResolve(const QString& moduleId, const QString& r
|
||||||
if (QFileInfo(unanchoredUrl.toLocalFile()).isFile()) {
|
if (QFileInfo(unanchoredUrl.toLocalFile()).isFile()) {
|
||||||
auto msg = QString("relative module ids must be anchored; use './%1' instead")
|
auto msg = QString("relative module ids must be anchored; use './%1' instead")
|
||||||
.arg(moduleId);
|
.arg(moduleId);
|
||||||
return throwResolveError(_engine->makeError(_engine->newValue(message.arg(msg))));
|
return throwResolveError(message.arg(msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return throwResolveError(_engine->makeError(_engine->newValue(message.arg("system module not found"))));
|
return throwResolveError(message.arg("system module not found"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url.isRelative()) {
|
if (url.isRelative()) {
|
||||||
return throwResolveError(_engine->makeError(_engine->newValue(message.arg("could not resolve module id"))));
|
return throwResolveError(message.arg("could not resolve module id"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// if it looks like a local file, verify that it's an allowed path and really a file
|
// if it looks like a local file, verify that it's an allowed path and really a file
|
||||||
|
@ -1317,22 +1320,21 @@ QString ScriptManager::_requireResolve(const QString& moduleId, const QString& r
|
||||||
|
|
||||||
bool disallowOutsideFiles = !PathUtils::defaultScriptsLocation().isParentOf(canonical) && !currentSandboxURL.isLocalFile();
|
bool disallowOutsideFiles = !PathUtils::defaultScriptsLocation().isParentOf(canonical) && !currentSandboxURL.isLocalFile();
|
||||||
if (disallowOutsideFiles && !PathUtils::isDescendantOf(canonical, currentSandboxURL)) {
|
if (disallowOutsideFiles && !PathUtils::isDescendantOf(canonical, currentSandboxURL)) {
|
||||||
return throwResolveError(_engine->makeError(_engine->newValue(message.arg(
|
return throwResolveError(message.arg(
|
||||||
QString("path '%1' outside of origin script '%2' '%3'")
|
QString("path '%1' outside of origin script '%2' '%3'")
|
||||||
.arg(PathUtils::stripFilename(url))
|
.arg(PathUtils::stripFilename(url))
|
||||||
.arg(PathUtils::stripFilename(currentSandboxURL))
|
.arg(PathUtils::stripFilename(currentSandboxURL))
|
||||||
.arg(canonical.toString())
|
.arg(canonical.toString())
|
||||||
))));
|
));
|
||||||
}
|
}
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
return throwResolveError(_engine->makeError(_engine->newValue(message.arg("path does not exist: " + url.toLocalFile()))));
|
return throwResolveError(message.arg("path does not exist: " + url.toLocalFile()));
|
||||||
}
|
}
|
||||||
if (!file.isFile()) {
|
if (!file.isFile()) {
|
||||||
return throwResolveError(_engine->makeError(_engine->newValue(message.arg("path is not a file: " + url.toLocalFile()))));
|
return throwResolveError(message.arg("path is not a file: " + url.toLocalFile()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_engine->maybeEmitUncaughtException(__FUNCTION__);
|
|
||||||
return url.toString();
|
return url.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1488,7 +1490,7 @@ ScriptValue ScriptManager::instantiateModule(const ScriptValue& module, const QS
|
||||||
//_engine->scriptValueDebugDetails(module);
|
//_engine->scriptValueDebugDetails(module);
|
||||||
result = _engine->evaluateInClosure(closure, _engine->newProgram( sourceCode, modulePath ));
|
result = _engine->evaluateInClosure(closure, _engine->newProgram( sourceCode, modulePath ));
|
||||||
}
|
}
|
||||||
_engine->maybeEmitUncaughtException(__FUNCTION__);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1510,9 +1512,8 @@ ScriptValue ScriptManager::require(const QString& moduleId) {
|
||||||
#ifdef DEBUG_JS_MODULES
|
#ifdef DEBUG_JS_MODULES
|
||||||
qCWarning(scriptengine_module) << "throwing module error:" << error.toString() << modulePath << error.property("stack").toString();
|
qCWarning(scriptengine_module) << "throwing module error:" << error.toString() << modulePath << error.property("stack").toString();
|
||||||
#endif
|
#endif
|
||||||
_engine->raiseException(error);
|
_engine->raiseException(error, "module error");
|
||||||
}
|
}
|
||||||
_engine->maybeEmitUncaughtException("module");
|
|
||||||
return _engine->nullValue();
|
return _engine->nullValue();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1520,7 +1521,6 @@ ScriptValue ScriptManager::require(const QString& moduleId) {
|
||||||
QString modulePath = _requireResolve(moduleId);
|
QString modulePath = _requireResolve(moduleId);
|
||||||
if (modulePath.isNull() || _engine->hasUncaughtException()) {
|
if (modulePath.isNull() || _engine->hasUncaughtException()) {
|
||||||
// the resolver already threw an exception -- bail early
|
// the resolver already threw an exception -- bail early
|
||||||
_engine->maybeEmitUncaughtException(__FUNCTION__);
|
|
||||||
return _engine->nullValue();
|
return _engine->nullValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1546,7 +1546,6 @@ ScriptValue ScriptManager::require(const QString& moduleId) {
|
||||||
qCDebug(scriptengine_module) << QString("require - using cached module for '%1' (loaded: %2)")
|
qCDebug(scriptengine_module) << QString("require - using cached module for '%1' (loaded: %2)")
|
||||||
.arg(moduleId).arg(module.property("loaded").toString());
|
.arg(moduleId).arg(module.property("loaded").toString());
|
||||||
registerModuleWithParent(module, parent);
|
registerModuleWithParent(module, parent);
|
||||||
_engine->maybeEmitUncaughtException("cached module");
|
|
||||||
return exports;
|
return exports;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1594,7 +1593,6 @@ ScriptValue ScriptManager::require(const QString& moduleId) {
|
||||||
|
|
||||||
//qCDebug(scriptengine_module) << "//ScriptManager::require(" << moduleId << ")";
|
//qCDebug(scriptengine_module) << "//ScriptManager::require(" << moduleId << ")";
|
||||||
|
|
||||||
_engine->maybeEmitUncaughtException(__FUNCTION__);
|
|
||||||
//qCDebug(scriptengine_module) << "Exports: " << _engine->scriptValueDebugDetails(module.property("exports"));
|
//qCDebug(scriptengine_module) << "Exports: " << _engine->scriptValueDebugDetails(module.property("exports"));
|
||||||
return module.property("exports");
|
return module.property("exports");
|
||||||
}
|
}
|
||||||
|
@ -1670,7 +1668,9 @@ void ScriptManager::include(const QStringList& includeFiles, const ScriptValue&
|
||||||
|
|
||||||
doWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, operation);
|
doWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, operation);
|
||||||
if(_engine->hasUncaughtException()) {
|
if(_engine->hasUncaughtException()) {
|
||||||
emit unhandledException(_engine->cloneUncaughtException("evaluateInclude"));
|
auto ex = _engine->uncaughtException();
|
||||||
|
ex->additionalInfo += "; evaluateInClosure";
|
||||||
|
emit unhandledException(ex);
|
||||||
_engine->clearExceptions();
|
_engine->clearExceptions();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1995,11 +1995,14 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c
|
||||||
//syntaxError.setProperty("detail", entityID.toString());
|
//syntaxError.setProperty("detail", entityID.toString());
|
||||||
//V8TODO
|
//V8TODO
|
||||||
//emit unhandledException(syntaxError);
|
//emit unhandledException(syntaxError);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!program) {
|
if (!program) {
|
||||||
setError("Bad program (isNull)", EntityScriptStatus::ERROR_RUNNING_SCRIPT);
|
setError("Bad program (isNull)", EntityScriptStatus::ERROR_RUNNING_SCRIPT);
|
||||||
emit unhandledException(_engine->makeError(_engine->newValue("program.isNull")));
|
std::shared_ptr<ScriptException> ex = std::make_shared<ScriptEngineException>("Program is Null", "Bad program in entityScriptContentAvailable");
|
||||||
|
emit unhandledException(ex);
|
||||||
|
|
||||||
return; // done processing script
|
return; // done processing script
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2163,7 +2166,10 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c
|
||||||
entityScriptObject = entityScriptConstructor.construct();
|
entityScriptObject = entityScriptConstructor.construct();
|
||||||
|
|
||||||
if (_engine->hasUncaughtException()) {
|
if (_engine->hasUncaughtException()) {
|
||||||
entityScriptObject = _engine->cloneUncaughtException("(construct " + entityID.toString() + ")");
|
// V8TODO: Why were we copying the uncaught exception here? Does anything
|
||||||
|
// actually make use of that?
|
||||||
|
// entityScriptObject = _engine->cloneUncaughtException("(construct " + entityID.toString() + ")");
|
||||||
|
entityScriptObject = _engine->nullValue();
|
||||||
_engine->clearExceptions();
|
_engine->clearExceptions();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -2171,9 +2177,10 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c
|
||||||
doWithEnvironment(entityID, sandboxURL, initialization);
|
doWithEnvironment(entityID, sandboxURL, initialization);
|
||||||
|
|
||||||
if (entityScriptObject.isError()) {
|
if (entityScriptObject.isError()) {
|
||||||
auto exception = entityScriptObject;
|
// auto exception = entityScriptObject;
|
||||||
setError(formatException(exception, _enableExtendedJSExceptions.get()), EntityScriptStatus::ERROR_RUNNING_SCRIPT);
|
// setError(formatException(exception, _enableExtendedJSExceptions.get()), EntityScriptStatus::ERROR_RUNNING_SCRIPT);
|
||||||
emit unhandledException(exception);
|
// emit unhandledException(exception);
|
||||||
|
// V8TODO: Is this needed? Wouldn't the ScriptManager have already emitted the exception?
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2340,7 +2347,6 @@ void ScriptManager::doWithEnvironment(const EntityItemID& entityID, const QUrl&
|
||||||
#else
|
#else
|
||||||
operation();
|
operation();
|
||||||
#endif
|
#endif
|
||||||
_engine->maybeEmitUncaughtException(!entityID.isNull() ? entityID.toString() : __FUNCTION__);
|
|
||||||
currentEntityIdentifier = oldIdentifier;
|
currentEntityIdentifier = oldIdentifier;
|
||||||
currentSandboxURL = oldSandboxURL;
|
currentSandboxURL = oldSandboxURL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
#include "Quat.h"
|
#include "Quat.h"
|
||||||
#include "ScriptUUID.h"
|
#include "ScriptUUID.h"
|
||||||
#include "ScriptValue.h"
|
#include "ScriptValue.h"
|
||||||
|
#include "ScriptException.h"
|
||||||
#include "Vec3.h"
|
#include "Vec3.h"
|
||||||
|
|
||||||
static const QString NO_SCRIPT("");
|
static const QString NO_SCRIPT("");
|
||||||
|
@ -1326,7 +1327,7 @@ signals:
|
||||||
*
|
*
|
||||||
* @param exception
|
* @param exception
|
||||||
*/
|
*/
|
||||||
void unhandledException(const ScriptValue& exception);
|
void unhandledException(std::shared_ptr<ScriptException> exception);
|
||||||
|
|
||||||
///@}
|
///@}
|
||||||
|
|
||||||
|
|
|
@ -30,5 +30,12 @@
|
||||||
connect(_manager, &ScriptManager::doneRunning, this, &ScriptManagerScriptingInterface::doneRunning);
|
connect(_manager, &ScriptManager::doneRunning, this, &ScriptManagerScriptingInterface::doneRunning);
|
||||||
connect(_manager, &ScriptManager::entityScriptDetailsUpdated, this, &ScriptManagerScriptingInterface::entityScriptDetailsUpdated);
|
connect(_manager, &ScriptManager::entityScriptDetailsUpdated, this, &ScriptManagerScriptingInterface::entityScriptDetailsUpdated);
|
||||||
connect(_manager, &ScriptManager::entityScriptPreloadFinished, this, &ScriptManagerScriptingInterface::entityScriptPreloadFinished);
|
connect(_manager, &ScriptManager::entityScriptPreloadFinished, this, &ScriptManagerScriptingInterface::entityScriptPreloadFinished);
|
||||||
connect(_manager, &ScriptManager::unhandledException, this, &ScriptManagerScriptingInterface::unhandledException);
|
connect(_manager, &ScriptManager::unhandledException, this, &ScriptManagerScriptingInterface::scriptManagerException);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptManagerScriptingInterface::scriptManagerException(std::shared_ptr<ScriptException> exception) {
|
||||||
|
// V8TODO: What should we actually handle here?
|
||||||
|
|
||||||
|
|
||||||
|
// emit unhandledException(exception.thrownValue);
|
||||||
}
|
}
|
|
@ -698,7 +698,11 @@ protected:
|
||||||
Q_INVOKABLE void entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success, const QString& status)
|
Q_INVOKABLE void entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success, const QString& status)
|
||||||
{ _manager->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success, status); }
|
{ _manager->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success, status); }
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void scriptManagerException(std::shared_ptr<ScriptException> exception);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
ScriptManager *_manager;
|
ScriptManager *_manager;
|
||||||
};
|
};
|
|
@ -71,20 +71,17 @@ bool ScriptEngineV8::IS_THREADSAFE_INVOCATION(const QThread* thread, const QStri
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// engine-aware JS Error copier and factory
|
ScriptValue ScriptEngineV8::makeError(const ScriptValue& _other, const QString& type) {
|
||||||
V8ScriptValue ScriptEngineV8::makeError(const V8ScriptValue& _other, const QString& type) {
|
|
||||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
||||||
v8::Locker locker(_v8Isolate);
|
return nullValue();
|
||||||
v8::Isolate::Scope isolateScope(_v8Isolate);
|
|
||||||
v8::HandleScope handleScope(_v8Isolate);
|
|
||||||
v8::Context::Scope contextScope(getContext());
|
|
||||||
return V8ScriptValue(this, v8::Null(_v8Isolate));
|
|
||||||
}
|
}
|
||||||
v8::Locker locker(_v8Isolate);
|
v8::Locker locker(_v8Isolate);
|
||||||
v8::Isolate::Scope isolateScope(_v8Isolate);
|
v8::Isolate::Scope isolateScope(_v8Isolate);
|
||||||
v8::HandleScope handleScope(_v8Isolate);
|
v8::HandleScope handleScope(_v8Isolate);
|
||||||
v8::Context::Scope contextScope(getContext());
|
v8::Context::Scope contextScope(getContext());
|
||||||
return V8ScriptValue(this, v8::Null(_v8Isolate));
|
return nullValue();
|
||||||
|
}
|
||||||
|
|
||||||
//V8TODO
|
//V8TODO
|
||||||
/*
|
/*
|
||||||
auto other = _other;
|
auto other = _other;
|
||||||
|
@ -117,33 +114,8 @@ V8ScriptValue ScriptEngineV8::makeError(const V8ScriptValue& _other, const QStri
|
||||||
err.setProperty(it.name(), it.value());
|
err.setProperty(it.name(), it.value());
|
||||||
}
|
}
|
||||||
return err;*/
|
return err;*/
|
||||||
}
|
//}
|
||||||
|
|
||||||
ScriptValue ScriptEngineV8::makeError(const ScriptValue& _other, const QString& type) {
|
|
||||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
|
||||||
return nullValue();
|
|
||||||
}
|
|
||||||
v8::Locker locker(_v8Isolate);
|
|
||||||
v8::Isolate::Scope isolateScope(_v8Isolate);
|
|
||||||
v8::HandleScope handleScope(_v8Isolate);
|
|
||||||
v8::Context::Scope contextScope(getContext());
|
|
||||||
return nullValue();
|
|
||||||
|
|
||||||
//V8TODO
|
|
||||||
//what does makeError actually do?
|
|
||||||
/*ScriptValueV8Wrapper* unwrapped = ScriptValueV8Wrapper::unwrap(_other);
|
|
||||||
V8ScriptValue other;
|
|
||||||
if (_other.isString()) {
|
|
||||||
other = QScriptEngine::newObject();
|
|
||||||
other.setProperty("message", _other.toString());
|
|
||||||
} else if (unwrapped) {
|
|
||||||
other = unwrapped->toV8Value();
|
|
||||||
} else {
|
|
||||||
other = QScriptEngine::newVariant(_other.toVariant());
|
|
||||||
}
|
|
||||||
V8ScriptValue result = makeError(other, type);
|
|
||||||
return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));*/
|
|
||||||
}
|
|
||||||
|
|
||||||
// check syntax and when there are issues returns an actual "SyntaxError" with the details
|
// check syntax and when there are issues returns an actual "SyntaxError" with the details
|
||||||
ScriptValue ScriptEngineV8::checkScriptSyntax(ScriptProgramPointer program) {
|
ScriptValue ScriptEngineV8::checkScriptSyntax(ScriptProgramPointer program) {
|
||||||
|
@ -200,82 +172,14 @@ ScriptValue ScriptEngineV8::checkScriptSyntax(ScriptProgramPointer program) {
|
||||||
return undefinedValue();
|
return undefinedValue();
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
// this pulls from the best available information to create a detailed snapshot of the current exception
|
|
||||||
ScriptValue ScriptEngineV8::cloneUncaughtException(const QString& extraDetail) {
|
|
||||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
|
||||||
return nullValue();
|
|
||||||
}
|
|
||||||
if (!hasUncaughtException()) {
|
|
||||||
return nullValue();
|
|
||||||
}
|
|
||||||
v8::Locker locker(_v8Isolate);
|
|
||||||
v8::Isolate::Scope isolateScope(_v8Isolate);
|
|
||||||
v8::HandleScope handleScope(_v8Isolate);
|
|
||||||
v8::Context::Scope contextScope(getContext());
|
|
||||||
return nullValue();
|
|
||||||
//V8TODO
|
|
||||||
/*
|
|
||||||
auto exception = uncaughtException();
|
|
||||||
// ensure the error object is engine-local
|
|
||||||
auto err = makeError(exception);
|
|
||||||
|
|
||||||
// not sure why Qt does't offer uncaughtExceptionFileName -- but the line number
|
|
||||||
// on its own is often useless/wrong if arbitrarily married to a filename.
|
|
||||||
// when the error object already has this info, it seems to be the most reliable
|
|
||||||
auto fileName = exception.property("fileName").toString();
|
|
||||||
auto lineNumber = exception.property("lineNumber").toInt32();
|
|
||||||
|
|
||||||
// the backtrace, on the other hand, seems most reliable taken from uncaughtExceptionBacktrace
|
|
||||||
auto backtrace = uncaughtExceptionBacktrace();
|
|
||||||
if (backtrace.isEmpty()) {
|
|
||||||
// fallback to the error object
|
|
||||||
backtrace = exception.property("stack").toString().split(ScriptManager::SCRIPT_BACKTRACE_SEP);
|
|
||||||
}
|
|
||||||
// the ad hoc "detail" property can be used now to embed additional clues
|
|
||||||
auto detail = exception.property("detail").toString();
|
|
||||||
if (detail.isEmpty()) {
|
|
||||||
detail = extraDetail;
|
|
||||||
} else if (!extraDetail.isEmpty()) {
|
|
||||||
detail += "(" + extraDetail + ")";
|
|
||||||
}
|
|
||||||
if (lineNumber <= 0) {
|
|
||||||
lineNumber = uncaughtExceptionLineNumber();
|
|
||||||
}
|
|
||||||
if (fileName.isEmpty()) {
|
|
||||||
// climb the stack frames looking for something useful to display
|
|
||||||
for (auto c = QScriptEngine::currentContext(); c && fileName.isEmpty(); c = c->parentContext()) {
|
|
||||||
V8ScriptContextInfo info{ c };
|
|
||||||
if (!info.fileName().isEmpty()) {
|
|
||||||
// take fileName:lineNumber as a pair
|
|
||||||
fileName = info.fileName();
|
|
||||||
lineNumber = info.lineNumber();
|
|
||||||
if (backtrace.isEmpty()) {
|
|
||||||
backtrace = c->backtrace();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err.setProperty("fileName", fileNlintame);
|
|
||||||
err.setProperty("lineNumber", lineNumber);
|
|
||||||
err.setProperty("detail", detail);
|
|
||||||
err.setProperty("stack", backtrace.join(ScriptManager::SCRIPT_BACKTRACE_SEP));
|
|
||||||
|
|
||||||
#ifdef DEBUG_JS_EXCEPTIONS
|
|
||||||
err.setProperty("_fileName", exception.property("fileName").toString());
|
|
||||||
err.setProperty("_stack", uncaughtExceptionBacktrace().join(SCRIPT_BACKTRACE_SEP));
|
|
||||||
err.setProperty("_lineNumber", uncaughtExceptionLineNumber());
|
|
||||||
#endif
|
|
||||||
return err;
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ScriptEngineV8::raiseException(const V8ScriptValue& exception) {
|
bool ScriptEngineV8::raiseException(const V8ScriptValue& exception) {
|
||||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//V8TODO
|
//V8TODO
|
||||||
_v8Isolate->ThrowException(makeError(exception).get());
|
// _v8Isolate->ThrowException(makeError(exception).get());
|
||||||
|
|
||||||
|
|
||||||
/*if (QScriptEngine::currentContext()) {
|
/*if (QScriptEngine::currentContext()) {
|
||||||
// we have an active context / JS stack frame so throw the exception per usual
|
// we have an active context / JS stack frame so throw the exception per usual
|
||||||
QScriptEngine::currentContext()->throwValue(makeError(exception));
|
QScriptEngine::currentContext()->throwValue(makeError(exception));
|
||||||
|
@ -290,22 +194,6 @@ bool ScriptEngineV8::raiseException(const V8ScriptValue& exception) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScriptEngineV8::maybeEmitUncaughtException(const QString& debugHint) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!isEvaluating() && hasUncaughtException()) {
|
|
||||||
emit exception(cloneUncaughtException(debugHint));
|
|
||||||
clearExceptions();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lambda
|
// Lambda
|
||||||
/*ScriptValue ScriptEngineV8::newLambdaFunction(std::function<V8ScriptValue(V8ScriptContext*, ScriptEngineV8*)> operation,
|
/*ScriptValue ScriptEngineV8::newLambdaFunction(std::function<V8ScriptValue(V8ScriptContext*, ScriptEngineV8*)> operation,
|
||||||
const V8ScriptValue& data,
|
const V8ScriptValue& data,
|
||||||
|
@ -1019,12 +907,11 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasUncaughtException()) {
|
if (hasUncaughtException()) {
|
||||||
auto err = cloneUncaughtException(__FUNCTION__);
|
|
||||||
#ifdef DEBUG_JS_EXCEPTIONS
|
#ifdef DEBUG_JS_EXCEPTIONS
|
||||||
qCWarning(shared) << __FUNCTION__ << "---------- hasCaught:" << err.toString() << result.toString();
|
qCWarning(shared) << __FUNCTION__ << "---------- hasCaught:" << err.toString() << result.toString();
|
||||||
err.setProperty("_result", result);
|
err.setProperty("_result", result);
|
||||||
#endif
|
#endif
|
||||||
result = err;
|
result = nullValue();
|
||||||
} else {
|
} else {
|
||||||
result = ScriptValue(new ScriptValueV8Wrapper(this, V8ScriptValue(this, v8Result)));
|
result = ScriptValue(new ScriptValueV8Wrapper(this, V8ScriptValue(this, v8Result)));
|
||||||
}
|
}
|
||||||
|
@ -1073,34 +960,9 @@ ScriptValue ScriptEngineV8::evaluate(const QString& sourceCode, const QString& f
|
||||||
v8::ScriptOrigin scriptOrigin(getIsolate(), v8::String::NewFromUtf8(getIsolate(), fileName.toStdString().c_str()).ToLocalChecked());
|
v8::ScriptOrigin scriptOrigin(getIsolate(), v8::String::NewFromUtf8(getIsolate(), fileName.toStdString().c_str()).ToLocalChecked());
|
||||||
v8::Local<v8::Script> script;
|
v8::Local<v8::Script> script;
|
||||||
if (!v8::Script::Compile(getContext(), v8::String::NewFromUtf8(getIsolate(), sourceCode.toStdString().c_str()).ToLocalChecked(), &scriptOrigin).ToLocal(&script)) {
|
if (!v8::Script::Compile(getContext(), v8::String::NewFromUtf8(getIsolate(), sourceCode.toStdString().c_str()).ToLocalChecked(), &scriptOrigin).ToLocal(&script)) {
|
||||||
//V8TODO replace this with external function
|
setUncaughtException(tryCatch, "Error while compiling script");
|
||||||
int errorColumnNumber = 0;
|
|
||||||
int errorLineNumber = 0;
|
|
||||||
QString errorMessage = "";
|
|
||||||
QString errorBacktrace = "";
|
|
||||||
//v8::String::Utf8Value utf8Value(getIsolate(), tryCatch.Exception());
|
|
||||||
v8::String::Utf8Value utf8Value(getIsolate(), tryCatch.Message()->Get());
|
|
||||||
errorMessage = QString(*utf8Value);
|
|
||||||
v8::Local<v8::Message> exceptionMessage = tryCatch.Message();
|
|
||||||
if (!exceptionMessage.IsEmpty()) {
|
|
||||||
errorLineNumber = exceptionMessage->GetLineNumber(getContext()).FromJust();
|
|
||||||
errorColumnNumber = 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);
|
|
||||||
errorBacktrace = *backtraceUtf8Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
qCDebug(scriptengine) << "Compiling script \"" << fileName << "\" failed on line " << errorLineNumber << " column " << errorColumnNumber << " with message: \"" << errorMessage <<"\" backtrace: " << errorBacktrace;
|
|
||||||
}
|
|
||||||
auto err = makeError(newValue(errorMessage));
|
|
||||||
raiseException(err);
|
|
||||||
maybeEmitUncaughtException("compile");
|
|
||||||
_evaluatingCounter--;
|
_evaluatingCounter--;
|
||||||
return err;
|
return nullValue();
|
||||||
}
|
}
|
||||||
//qCDebug(scriptengine) << "Script compilation succesful: " << fileName;
|
//qCDebug(scriptengine) << "Script compilation succesful: " << fileName;
|
||||||
|
|
||||||
|
@ -1145,6 +1007,12 @@ 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::setUncaughtEngineException(const QString &reason, const QString& info) {
|
||||||
|
auto ex = std::make_shared<ScriptEngineException>(reason, info);
|
||||||
|
setUncaughtException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
void ScriptEngineV8::setUncaughtException(const v8::TryCatch &tryCatch, const QString& info) {
|
void ScriptEngineV8::setUncaughtException(const v8::TryCatch &tryCatch, const QString& info) {
|
||||||
if (!tryCatch.HasCaught()) {
|
if (!tryCatch.HasCaught()) {
|
||||||
qCWarning(scriptengine) << "setUncaughtException called without exception";
|
qCWarning(scriptengine) << "setUncaughtException called without exception";
|
||||||
|
@ -1152,8 +1020,8 @@ void ScriptEngineV8::setUncaughtException(const v8::TryCatch &tryCatch, const QS
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptException ex;
|
auto ex = std::make_shared<ScriptException>();
|
||||||
ex.additionalInfo = info;
|
ex->additionalInfo = info;
|
||||||
|
|
||||||
v8::Locker locker(_v8Isolate);
|
v8::Locker locker(_v8Isolate);
|
||||||
v8::Isolate::Scope isolateScope(_v8Isolate);
|
v8::Isolate::Scope isolateScope(_v8Isolate);
|
||||||
|
@ -1166,30 +1034,35 @@ void ScriptEngineV8::setUncaughtException(const v8::TryCatch &tryCatch, const QS
|
||||||
//v8::String::Utf8Value utf8Value(getIsolate(), tryCatch.Exception());
|
//v8::String::Utf8Value utf8Value(getIsolate(), tryCatch.Exception());
|
||||||
v8::String::Utf8Value utf8Value(getIsolate(), tryCatch.Message()->Get());
|
v8::String::Utf8Value utf8Value(getIsolate(), tryCatch.Message()->Get());
|
||||||
|
|
||||||
ex.errorMessage = QString(*utf8Value);
|
ex->errorMessage = QString(*utf8Value);
|
||||||
|
|
||||||
v8::Local<v8::Message> exceptionMessage = tryCatch.Message();
|
v8::Local<v8::Message> exceptionMessage = tryCatch.Message();
|
||||||
if (!exceptionMessage.IsEmpty()) {
|
if (!exceptionMessage.IsEmpty()) {
|
||||||
ex.errorLine = exceptionMessage->GetLineNumber(getContext()).FromJust();
|
ex->errorLine = exceptionMessage->GetLineNumber(getContext()).FromJust();
|
||||||
ex.errorColumn = exceptionMessage->GetStartColumn(getContext()).FromJust();
|
ex->errorColumn = exceptionMessage->GetStartColumn(getContext()).FromJust();
|
||||||
v8::Local<v8::Value> backtraceV8String;
|
v8::Local<v8::Value> backtraceV8String;
|
||||||
if (tryCatch.StackTrace(getContext()).ToLocal(&backtraceV8String)) {
|
if (tryCatch.StackTrace(getContext()).ToLocal(&backtraceV8String)) {
|
||||||
if (backtraceV8String->IsString()) {
|
if (backtraceV8String->IsString()) {
|
||||||
if (v8::Local<v8::String>::Cast(backtraceV8String)->Length() > 0) {
|
if (v8::Local<v8::String>::Cast(backtraceV8String)->Length() > 0) {
|
||||||
v8::String::Utf8Value backtraceUtf8Value(getIsolate(), backtraceV8String);
|
v8::String::Utf8Value backtraceUtf8Value(getIsolate(), backtraceV8String);
|
||||||
QString errorBacktrace = *backtraceUtf8Value;
|
QString errorBacktrace = *backtraceUtf8Value;
|
||||||
ex.backtrace = errorBacktrace.split("\n");
|
ex->backtrace = errorBacktrace.split("\n");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(scriptengine) << "Emitting exception:" << ex;
|
setUncaughtException(ex);
|
||||||
_uncaughtException = ex;
|
|
||||||
emit exception(ex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScriptEngineV8::setUncaughtException(std::shared_ptr<ScriptException> uncaughtException) {
|
||||||
|
qCDebug(scriptengine) << "Emitting exception:" << uncaughtException;
|
||||||
|
_uncaughtException = uncaughtException;
|
||||||
|
|
||||||
|
auto copy = uncaughtException->clone();
|
||||||
|
emit exception(copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
QString ScriptEngineV8::formatErrorMessageFromTryCatch(v8::TryCatch &tryCatch) {
|
QString ScriptEngineV8::formatErrorMessageFromTryCatch(v8::TryCatch &tryCatch) {
|
||||||
|
@ -1274,18 +1147,14 @@ Q_INVOKABLE ScriptValue ScriptEngineV8::evaluate(const ScriptProgramPointer& pro
|
||||||
v8::Context::Scope contextScope(getContext());
|
v8::Context::Scope contextScope(getContext());
|
||||||
ScriptProgramV8Wrapper* unwrapped = ScriptProgramV8Wrapper::unwrap(program);
|
ScriptProgramV8Wrapper* unwrapped = ScriptProgramV8Wrapper::unwrap(program);
|
||||||
if (!unwrapped) {
|
if (!unwrapped) {
|
||||||
errorValue = makeError(newValue("could not unwrap program"));
|
setUncaughtEngineException("Could not unwrap program", "Compile error");
|
||||||
raiseException(errorValue);
|
|
||||||
maybeEmitUncaughtException("compile");
|
|
||||||
hasFailed = true;
|
hasFailed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!hasFailed) {
|
if(!hasFailed) {
|
||||||
ScriptSyntaxCheckResultPointer syntaxCheck = unwrapped->checkSyntax();
|
ScriptSyntaxCheckResultPointer syntaxCheck = unwrapped->checkSyntax();
|
||||||
if (syntaxCheck->state() == ScriptSyntaxCheckResult::Error) {
|
if (syntaxCheck->state() == ScriptSyntaxCheckResult::Error) {
|
||||||
errorValue = makeError(newValue(syntaxCheck->errorMessage()));
|
setUncaughtEngineException(syntaxCheck->errorMessage(), "Compile error");
|
||||||
raiseException(errorValue);
|
|
||||||
maybeEmitUncaughtException("compile");
|
|
||||||
hasFailed = true;
|
hasFailed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1307,8 +1176,7 @@ Q_INVOKABLE ScriptValue ScriptEngineV8::evaluate(const ScriptProgramPointer& pro
|
||||||
Q_ASSERT(tryCatchRun.HasCaught());
|
Q_ASSERT(tryCatchRun.HasCaught());
|
||||||
auto runError = tryCatchRun.Message();
|
auto runError = tryCatchRun.Message();
|
||||||
errorValue = ScriptValue(new ScriptValueV8Wrapper(this, V8ScriptValue(this, runError->Get())));
|
errorValue = ScriptValue(new ScriptValueV8Wrapper(this, V8ScriptValue(this, runError->Get())));
|
||||||
raiseException(errorValue);
|
raiseException(errorValue, "evaluation error");
|
||||||
maybeEmitUncaughtException("evaluate");
|
|
||||||
hasFailed = true;
|
hasFailed = true;
|
||||||
} else {
|
} else {
|
||||||
// V8TODO this is just to check if run will always return false for uncaught exception
|
// V8TODO this is just to check if run will always return false for uncaught exception
|
||||||
|
@ -1529,7 +1397,7 @@ void ScriptEngineV8::abortEvaluation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEngineV8::clearExceptions() {
|
void ScriptEngineV8::clearExceptions() {
|
||||||
_uncaughtException = ScriptException();
|
_uncaughtException.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptContext* ScriptEngineV8::currentContext() const {
|
ScriptContext* ScriptEngineV8::currentContext() const {
|
||||||
|
@ -1551,7 +1419,7 @@ ScriptContext* ScriptEngineV8::currentContext() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScriptEngineV8::hasUncaughtException() const {
|
bool ScriptEngineV8::hasUncaughtException() const {
|
||||||
return !_uncaughtException.isEmpty();
|
return _uncaughtException != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScriptEngineV8::isEvaluating() const {
|
bool ScriptEngineV8::isEvaluating() const {
|
||||||
|
@ -1667,11 +1535,15 @@ void ScriptEngineV8::setThread(QThread* thread) {
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
|
||||||
ScriptException ScriptEngineV8::uncaughtException() const {
|
std::shared_ptr<ScriptException> ScriptEngineV8::uncaughtException() const {
|
||||||
return _uncaughtException;
|
return _uncaughtException->clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScriptEngineV8::raiseException(const ScriptValue& exception) {
|
bool ScriptEngineV8::raiseException(const QString& error, const QString &reason) {
|
||||||
|
return raiseException(ScriptValue(), reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScriptEngineV8::raiseException(const ScriptValue& exception, const QString &reason) {
|
||||||
//V8TODO
|
//V8TODO
|
||||||
//Q_ASSERT(false);
|
//Q_ASSERT(false);
|
||||||
// qCritical() << "Script exception occurred: " << exception.toString();
|
// qCritical() << "Script exception occurred: " << exception.toString();
|
||||||
|
@ -1681,6 +1553,7 @@ bool ScriptEngineV8::raiseException(const ScriptValue& exception) {
|
||||||
// emit
|
// emit
|
||||||
//return raiseException(qException);
|
//return raiseException(qException);
|
||||||
|
|
||||||
|
qCCritical(scriptengine) << "Raise exception for reason" << reason << "NOT IMPLEMENTED!";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,6 @@ public: // construction
|
||||||
public: // ScriptEngine implementation
|
public: // ScriptEngine implementation
|
||||||
virtual void abortEvaluation() override;
|
virtual void abortEvaluation() override;
|
||||||
virtual void clearExceptions() override;
|
virtual void clearExceptions() override;
|
||||||
virtual ScriptValue cloneUncaughtException(const QString& detail = QString()) override;
|
|
||||||
virtual ScriptContext* currentContext() const override;
|
virtual ScriptContext* currentContext() const override;
|
||||||
Q_INVOKABLE virtual ScriptValue evaluate(const QString& program, const QString& fileName = QString()) override;
|
Q_INVOKABLE virtual ScriptValue evaluate(const QString& program, const QString& fileName = QString()) override;
|
||||||
Q_INVOKABLE virtual ScriptValue evaluate(const ScriptProgramPointer& program) override;
|
Q_INVOKABLE virtual ScriptValue evaluate(const ScriptProgramPointer& program) override;
|
||||||
|
@ -81,11 +80,6 @@ public: // ScriptEngine implementation
|
||||||
virtual bool isEvaluating() const override;
|
virtual bool isEvaluating() const override;
|
||||||
//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;
|
|
||||||
|
|
||||||
|
|
||||||
// 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 ScriptValue newArray(uint length = 0) override;
|
virtual ScriptValue newArray(uint length = 0) override;
|
||||||
virtual ScriptValue newArrayBuffer(const QByteArray& message) override;
|
virtual ScriptValue newArrayBuffer(const QByteArray& message) override;
|
||||||
|
@ -106,7 +100,11 @@ public: // ScriptEngine implementation
|
||||||
virtual ScriptValue newValue(const char* value) override;
|
virtual ScriptValue newValue(const char* value) override;
|
||||||
virtual ScriptValue newVariant(const QVariant& value) override;
|
virtual ScriptValue newVariant(const QVariant& value) override;
|
||||||
virtual ScriptValue nullValue() override;
|
virtual ScriptValue nullValue() override;
|
||||||
virtual bool raiseException(const ScriptValue& exception) override;
|
|
||||||
|
virtual ScriptValue makeError(const ScriptValue& other, const QString& type = "Error") override;
|
||||||
|
|
||||||
|
virtual bool raiseException(const QString& exception, const QString &reason = QString()) override;
|
||||||
|
virtual bool raiseException(const ScriptValue& exception, const QString &reason = QString()) override;
|
||||||
Q_INVOKABLE virtual void registerEnum(const QString& enumName, QMetaEnum newEnum) override;
|
Q_INVOKABLE virtual void registerEnum(const QString& enumName, QMetaEnum newEnum) override;
|
||||||
Q_INVOKABLE virtual void registerFunction(const QString& name,
|
Q_INVOKABLE virtual void registerFunction(const QString& name,
|
||||||
ScriptEngine::FunctionSignature fun,
|
ScriptEngine::FunctionSignature fun,
|
||||||
|
@ -128,7 +126,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 ScriptException uncaughtException() const override;
|
virtual std::shared_ptr<ScriptException> uncaughtException() 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;
|
||||||
|
@ -142,7 +140,7 @@ public: // ScriptEngine implementation
|
||||||
inline bool IS_THREADSAFE_INVOCATION(const QString& method) { return ScriptEngine::IS_THREADSAFE_INVOCATION(method); }
|
inline bool IS_THREADSAFE_INVOCATION(const QString& method) { return ScriptEngine::IS_THREADSAFE_INVOCATION(method); }
|
||||||
|
|
||||||
protected: // brought over from BaseScriptEngine
|
protected: // brought over from BaseScriptEngine
|
||||||
V8ScriptValue makeError(const V8ScriptValue& other, const QString& type = "Error");
|
|
||||||
|
|
||||||
// if the currentContext() is valid then throw the passed exception; otherwise, immediately emit it.
|
// if the currentContext() is valid then throw the passed exception; otherwise, immediately emit it.
|
||||||
// note: this is used in cases where C++ code might call into JS API methods directly
|
// note: this is used in cases where C++ code might call into JS API methods directly
|
||||||
|
@ -223,16 +221,18 @@ signals:
|
||||||
*
|
*
|
||||||
* @param exception Exception that was thrown
|
* @param exception Exception that was thrown
|
||||||
*/
|
*/
|
||||||
void exception(const ScriptException &exception);
|
void exception(std::shared_ptr<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 setUncaughtEngineException(const QString &message, const QString& info = QString());
|
||||||
void setUncaughtException(const v8::TryCatch &tryCatch, const QString& info = QString());
|
void setUncaughtException(const v8::TryCatch &tryCatch, const QString& info = QString());
|
||||||
|
void setUncaughtException(std::shared_ptr<ScriptException> exception);
|
||||||
|
|
||||||
ScriptException _uncaughtException;
|
std::shared_ptr<ScriptException> _uncaughtException;
|
||||||
|
|
||||||
|
|
||||||
// V8TODO: clean up isolate when script engine is destroyed?
|
// V8TODO: clean up isolate when script engine is destroyed?
|
||||||
|
|
1
tests/script-engine/src/tests/010_exception.js
Normal file
1
tests/script-engine/src/tests/010_exception.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
foo();
|
Loading…
Reference in a new issue