diff --git a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.cpp index 7f0e80cbae..2f20cd82c6 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.cpp @@ -6,10 +6,43 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// NOTE: we don't need to include this header unless/until we add additional symbols. -// By removing this header we prevent these warnings on windows: -// -// warning LNK4221: This object file does not define any previously undefined public symbols, -// so it will not be used by any link operation that consumes this library -// -//#include "JSEndpoint.h" \ No newline at end of file +#include "JSEndpoint.h" +#include "../../Logging.h" + +using namespace controller; + +QString formatException(const QJSValue& exception) { + QString note { "UncaughtException" }; + QString result; + + const auto message = exception.toString(); + const auto fileName = exception.property("fileName").toString(); + const auto lineNumber = exception.property("lineNumber").toString(); + const auto stacktrace = exception.property("stack").toString(); + + const QString SCRIPT_EXCEPTION_FORMAT = "[%0] %1 in %2:%3"; + const QString SCRIPT_BACKTRACE_SEP = "\n "; + + result = QString(SCRIPT_EXCEPTION_FORMAT).arg(note, message, fileName, lineNumber); + if (!stacktrace.isEmpty()) { + result += QString("\n[Backtrace]%1%2").arg(SCRIPT_BACKTRACE_SEP).arg(stacktrace); + } + return result; +} + +float JSEndpoint::peek() const { + QJSValue result = _callable.call(); + if (result.isError()) { + qCDebug(controllers).noquote() << formatException(result); + return 0.0f; + } else { + return (float)result.toNumber(); + } +} + +void JSEndpoint::apply(float newValue, const Pointer& source) { + QJSValue result = _callable.call(QJSValueList({ QJSValue(newValue) })); + if (result.isError()) { + qCDebug(controllers).noquote() << formatException(result); + } +} diff --git a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h index 24d5ec93e9..4d179da8e6 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h @@ -24,16 +24,11 @@ public: : Endpoint(Input::INVALID_INPUT), _callable(callable) { } - virtual float peek() const override { - return (float)const_cast(this)->_callable.call().toNumber(); - } - - virtual void apply(float newValue, const Pointer& source) override { - _callable.call(QJSValueList({ QJSValue(newValue) })); - } + virtual float peek() const override; + virtual void apply(float newValue, const Pointer& source) override; private: - QJSValue _callable; + mutable QJSValue _callable; }; } diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp index 3e7fde347e..e2c48d776f 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp @@ -7,6 +7,7 @@ // #include "ScriptEndpoint.h" +#include "../../Logging.h" #include @@ -14,6 +15,25 @@ using namespace controller; +QString formatException(const QScriptValue& exception) { + QString note { "UncaughtException" }; + QString result; + + const auto message = exception.toString(); + const auto fileName = exception.property("fileName").toString(); + const auto lineNumber = exception.property("lineNumber").toString(); + const auto stacktrace = exception.property("stack").toString(); + + const QString SCRIPT_EXCEPTION_FORMAT = "[%0] %1 in %2:%3"; + const QString SCRIPT_BACKTRACE_SEP = "\n "; + + result = QString(SCRIPT_EXCEPTION_FORMAT).arg(note, message, fileName, lineNumber); + if (!stacktrace.isEmpty()) { + result += QString("\n[Backtrace]%1%2").arg(SCRIPT_BACKTRACE_SEP).arg(stacktrace); + } + return result; +} + float ScriptEndpoint::peek() const { const_cast(this)->updateValue(); return _lastValueRead; @@ -26,10 +46,11 @@ void ScriptEndpoint::updateValue() { } QScriptValue result = _callable.call(); - - // If the callable ever returns a non-number, we assume it's a pose - // and start reporting ourselves as a pose. - if (result.isNumber()) { + if (result.isError()) { + // print JavaScript exception + qCDebug(controllers).noquote() << formatException(result); + _lastValueRead = 0.0f; + } else if (result.isNumber()) { _lastValueRead = (float)_callable.call().toNumber(); } else { Pose::fromScriptValue(result, _lastPoseRead); @@ -52,8 +73,12 @@ void ScriptEndpoint::internalApply(float value, int sourceID) { Q_ARG(int, sourceID)); return; } - _callable.call(QScriptValue(), + QScriptValue result = _callable.call(QScriptValue(), QScriptValueList({ QScriptValue(value), QScriptValue(sourceID) })); + if (result.isError()) { + // print JavaScript exception + qCDebug(controllers).noquote() << formatException(result); + } } Pose ScriptEndpoint::peekPose() const { @@ -67,6 +92,10 @@ void ScriptEndpoint::updatePose() { return; } QScriptValue result = _callable.call(); + if (result.isError()) { + // print JavaScript exception + qCDebug(controllers).noquote() << formatException(result); + } Pose::fromScriptValue(result, _lastPoseRead); } @@ -85,6 +114,10 @@ void ScriptEndpoint::internalApply(const Pose& newPose, int sourceID) { Q_ARG(int, sourceID)); return; } - _callable.call(QScriptValue(), + QScriptValue result = _callable.call(QScriptValue(), QScriptValueList({ Pose::toScriptValue(_callable.engine(), newPose), QScriptValue(sourceID) })); + if (result.isError()) { + // print JavaScript exception + qCDebug(controllers).noquote() << formatException(result); + } }