diff --git a/src/controllers/scripting/controllerscriptenginebase.cpp b/src/controllers/scripting/controllerscriptenginebase.cpp index 3d4dd9a7166..b9e13c8b7ba 100644 --- a/src/controllers/scripting/controllerscriptenginebase.cpp +++ b/src/controllers/scripting/controllerscriptenginebase.cpp @@ -6,6 +6,7 @@ #include "errordialoghandler.h" #include "mixer/playermanager.h" #include "moc_controllerscriptenginebase.cpp" +#include "util/cmdlineargs.h" ControllerScriptEngineBase::ControllerScriptEngineBase( Controller* controller, const RuntimeLoggingCategory& logger) @@ -13,6 +14,7 @@ ControllerScriptEngineBase::ControllerScriptEngineBase( m_pJSEngine(nullptr), m_pController(controller), m_logger(logger), + m_bAbortOnWarning(false), m_bTesting(false) { // Handle error dialog buttons qRegisterMetaType("QMessageBox::StandardButton"); @@ -23,6 +25,8 @@ bool ControllerScriptEngineBase::initialize() { return false; } + m_bAbortOnWarning = CmdlineArgs::Instance().getControllerAbortOnWarning(); + // Create the Script Engine m_pJSEngine = std::make_shared(this); diff --git a/src/controllers/scripting/controllerscriptenginebase.h b/src/controllers/scripting/controllerscriptenginebase.h index 2bccd8f031c..3d5f6c8a2d9 100644 --- a/src/controllers/scripting/controllerscriptenginebase.h +++ b/src/controllers/scripting/controllerscriptenginebase.h @@ -32,6 +32,10 @@ class ControllerScriptEngineBase : public QObject { void showScriptExceptionDialog(const QJSValue& evaluationResult, bool bFatal = false); void throwJSError(const QString& message); + bool willAbortOnWarning() const { + return m_bAbortOnWarning; + } + inline void setTesting(bool testing) { m_bTesting = testing; }; @@ -51,6 +55,8 @@ class ControllerScriptEngineBase : public QObject { Controller* m_pController; const RuntimeLoggingCategory m_logger; + bool m_bAbortOnWarning; + bool m_bTesting; protected slots: diff --git a/src/controllers/scripting/legacy/controllerscriptinterfacelegacy.cpp b/src/controllers/scripting/legacy/controllerscriptinterfacelegacy.cpp index 00abff10020..20e322cb31f 100644 --- a/src/controllers/scripting/legacy/controllerscriptinterfacelegacy.cpp +++ b/src/controllers/scripting/legacy/controllerscriptinterfacelegacy.cpp @@ -110,8 +110,7 @@ ControlObjectScript* ControllerScriptInterfaceLegacy::getControlObjectScript( double ControllerScriptInterfaceLegacy::getValue(const QString& group, const QString& name) { ControlObjectScript* coScript = getControlObjectScript(group, name); if (coScript == nullptr) { - qCWarning(m_logger) << "Unknown control" << group << name - << ", returning 0.0"; + logOrThrowError(QStringLiteral("Unknown control (%1, %2) returning 0.0").arg(group, name)); return 0.0; } return coScript->get(); @@ -120,8 +119,9 @@ double ControllerScriptInterfaceLegacy::getValue(const QString& group, const QSt void ControllerScriptInterfaceLegacy::setValue( const QString& group, const QString& name, double newValue) { if (util_isnan(newValue)) { - qCWarning(m_logger) << "script setting [" << group << "," - << name << "] to NotANumber, ignoring."; + logOrThrowError(QStringLiteral( + "Script tried setting (%1, %2) to NotANumber (NaN)") + .arg(group, name)); return; } @@ -141,8 +141,7 @@ void ControllerScriptInterfaceLegacy::setValue( double ControllerScriptInterfaceLegacy::getParameter(const QString& group, const QString& name) { ControlObjectScript* coScript = getControlObjectScript(group, name); if (coScript == nullptr) { - qCWarning(m_logger) << "Unknown control" << group << name - << ", returning 0.0"; + logOrThrowError(QStringLiteral("Unknown control (%1, %2) returning 0.0").arg(group, name)); return 0.0; } return coScript->getParameter(); @@ -151,8 +150,9 @@ double ControllerScriptInterfaceLegacy::getParameter(const QString& group, const void ControllerScriptInterfaceLegacy::setParameter( const QString& group, const QString& name, double newParameter) { if (util_isnan(newParameter)) { - qCWarning(m_logger) << "script setting [" << group << "," - << name << "] to NotANumber, ignoring."; + logOrThrowError(QStringLiteral( + "Script tried setting (%1, %2) to NotANumber (NaN)") + .arg(group, name)); return; } @@ -170,16 +170,16 @@ void ControllerScriptInterfaceLegacy::setParameter( double ControllerScriptInterfaceLegacy::getParameterForValue( const QString& group, const QString& name, double value) { if (util_isnan(value)) { - qCWarning(m_logger) << "script setting [" << group << "," - << name << "] to NotANumber, ignoring."; + logOrThrowError(QStringLiteral( + "Script tried setting (%1, %2) to NotANumber (NaN)") + .arg(group, name)); return 0.0; } ControlObjectScript* coScript = getControlObjectScript(group, name); if (coScript == nullptr) { - qCWarning(m_logger) << "Unknown control" << group << name - << ", returning 0.0"; + logOrThrowError(QStringLiteral("Unknown control (%1, %2) returning 0.0").arg(group, name)); return 0.0; } @@ -197,8 +197,7 @@ double ControllerScriptInterfaceLegacy::getDefaultValue(const QString& group, co ControlObjectScript* coScript = getControlObjectScript(group, name); if (coScript == nullptr) { - qCWarning(m_logger) << "Unknown control" << group << name - << ", returning 0.0"; + logOrThrowError(QStringLiteral("Unknown control (%1, %2) returning 0.0").arg(group, name)); return 0.0; } @@ -210,8 +209,7 @@ double ControllerScriptInterfaceLegacy::getDefaultParameter( ControlObjectScript* coScript = getControlObjectScript(group, name); if (coScript == nullptr) { - qCWarning(m_logger) << "Unknown control" << group << name - << ", returning 0.0"; + logOrThrowError(QStringLiteral("Unknown control (%1, %2) returning 0.0").arg(group, name)); return 0.0; } @@ -240,19 +238,19 @@ QJSValue ControllerScriptInterfaceLegacy::makeConnectionInternal( // The test setups do not run all of Mixxx, so ControlObjects not // existing during tests is okay. if (!m_pScriptEngineLegacy->isTesting()) { - m_pScriptEngineLegacy->throwJSError( - "script tried to connect to " - "ControlObject (" + - group + ", " + name + ") which is non-existent."); + logOrThrowError( + QStringLiteral("script tried to connect to ControlObject " + "(%1, %2) which is non-existent.") + .arg(group, name)); } return QJSValue(); } if (!callback.isCallable()) { - m_pScriptEngineLegacy->throwJSError("Tried to connect (" + group + ", " + name + - ")" + - " to an invalid callback. Make sure that your code contains no " - "syntax errors."); + logOrThrowError(QStringLiteral( + "Tried to connect (%1, %2) to an invalid callback. Make sure " + "that your code contains no syntax errors.") + .arg(group, name)); return QJSValue(); } @@ -293,6 +291,10 @@ void ControllerScriptInterfaceLegacy::triggerScriptConnection( ControlObjectScript* coScript = getControlObjectScript(connection.key.group, connection.key.item); if (coScript == nullptr) { + logOrThrowError(QStringLiteral( + "Script tried to trigger (%1, %2) which is non-existent.") + .arg(connection.key.group, + connection.key.item)); return; } @@ -311,6 +313,11 @@ QJSValue ControllerScriptInterfaceLegacy::connectControl(const QString& group, const QString& name, const QJSValue& passedCallback, bool disconnect) { + logOrThrowError(QStringLiteral( + "Script tried to connect to (%1, %2) using `connectControl` which " + "is deprecated. Use `makeConnection` instead!") + .arg(group, name)); + // The passedCallback may or may not actually be a function, so when // the actual callback function is found, store it in this variable. QJSValue actualCallbackFunction; @@ -427,18 +434,33 @@ QJSValue ControllerScriptInterfaceLegacy::connectControl(const QString& group, void ControllerScriptInterfaceLegacy::trigger(const QString& group, const QString& name) { ControlObjectScript* coScript = getControlObjectScript(group, name); - if (coScript != nullptr) { - coScript->emitValueChanged(); + if (coScript == nullptr) { + logOrThrowError(QStringLiteral( + "Script tried to trigger (%1, %2) which is non-existent.") + .arg(group, name)); + return; + } + coScript->emitValueChanged(); +} + +void ControllerScriptInterfaceLegacy::logOrThrowError(const QString& errorMessage) const { + if (m_pScriptEngineLegacy->willAbortOnWarning()) { + m_pScriptEngineLegacy->throwJSError(errorMessage); + } else { + qCWarning(m_logger) << errorMessage; } } void ControllerScriptInterfaceLegacy::log(const QString& message) { - qCDebug(m_logger) << "engine.log is deprecated. Use console.log instead."; + logOrThrowError(QStringLiteral("`engine.log` is deprecated. Use `console.log` instead!")); qCDebug(m_logger) << message; } int ControllerScriptInterfaceLegacy::beginTimer( int intervalMillis, QJSValue timerCallback, bool oneShot) { if (timerCallback.isString()) { + logOrThrowError( + QStringLiteral("passed a string to `engine.beginTimer`, please " + "pass a function instead!")); timerCallback = m_pScriptEngineLegacy->jsEngine()->evaluate(timerCallback.toString()); } else if (!timerCallback.isCallable()) { QString sErrorMessage( @@ -467,7 +489,7 @@ int ControllerScriptInterfaceLegacy::beginTimer( info.oneShot = oneShot; m_timers[timerId] = info; if (timerId == 0) { - qCWarning(m_logger) << "Script timer could not be created"; + logOrThrowError(QStringLiteral("Script timer could not be created")); } else if (oneShot) { qCDebug(m_logger) << "Starting one-shot timer:" << timerId; } else { @@ -478,8 +500,9 @@ int ControllerScriptInterfaceLegacy::beginTimer( void ControllerScriptInterfaceLegacy::stopTimer(int timerId) { if (!m_timers.contains(timerId)) { - qCWarning(m_logger) << "Killing timer" << timerId - << ": That timer does not exist!"; + logOrThrowError(QStringLiteral( + "Tried to kill Timer \"%1\" that does not exists") + .arg(timerId)); return; } qCDebug(m_logger) << "Killing timer:" << timerId; diff --git a/src/controllers/scripting/legacy/controllerscriptinterfacelegacy.h b/src/controllers/scripting/legacy/controllerscriptinterfacelegacy.h index 8dc78254cdf..7a3cd4b16f5 100644 --- a/src/controllers/scripting/legacy/controllerscriptinterfacelegacy.h +++ b/src/controllers/scripting/legacy/controllerscriptinterfacelegacy.h @@ -82,6 +82,7 @@ class ControllerScriptInterfaceLegacy : public QObject { bool skipSuperseded = false); QHash m_controlCache; ControlObjectScript* getControlObjectScript(const QString& group, const QString& name); + void logOrThrowError(const QString& errorMessage) const; SoftTakeoverCtrl m_st; diff --git a/src/util/cmdlineargs.cpp b/src/util/cmdlineargs.cpp index bc2dfc3b890..f3a27898ab3 100644 --- a/src/util/cmdlineargs.cpp +++ b/src/util/cmdlineargs.cpp @@ -20,6 +20,7 @@ CmdlineArgs::CmdlineArgs() : m_startInFullscreen(false), // Initialize vars m_controllerDebug(false), + m_controllerAbortOnWarning(false), m_developer(false), m_safeMode(false), m_useVuMeterGL(true), @@ -193,6 +194,17 @@ bool CmdlineArgs::parse(const QStringList& arguments, CmdlineArgs::ParseMode mod parser.addOption(controllerDebug); parser.addOption(controllerDebugDeprecated); + const QCommandLineOption controllerAbortOnWarning( + QStringLiteral("controller-abort-on-warning"), + forUserFeedback ? QCoreApplication::translate("CmdlineArgs", + "The controller mapping will issue more " + "aggressive warnings and errors when " + "detecting misuse of controller APIs. " + "New Controller Mappings should be " + "developed with this option enabled!") + : QString()); + parser.addOption(controllerAbortOnWarning); + const QCommandLineOption developer(QStringLiteral("developer"), forUserFeedback ? QCoreApplication::translate("CmdlineArgs", "Enables developer-mode. Includes extra log info, stats on " @@ -332,6 +344,7 @@ bool CmdlineArgs::parse(const QStringList& arguments, CmdlineArgs::ParseMode mod m_useVuMeterGL = !(parser.isSet(disableVuMeterGL) || parser.isSet(disableVuMeterGLDeprecated)); m_controllerDebug = parser.isSet(controllerDebug) || parser.isSet(controllerDebugDeprecated); + m_controllerAbortOnWarning = parser.isSet(controllerAbortOnWarning); m_developer = parser.isSet(developer); m_safeMode = parser.isSet(safeMode) || parser.isSet(safeModeDeprecated); m_debugAssertBreak = parser.isSet(debugAssertBreak) || parser.isSet(debugAssertBreakDeprecated); diff --git a/src/util/cmdlineargs.h b/src/util/cmdlineargs.h index 6d7c3be7116..7ec059b843b 100644 --- a/src/util/cmdlineargs.h +++ b/src/util/cmdlineargs.h @@ -34,6 +34,9 @@ class CmdlineArgs final { bool getControllerDebug() const { return m_controllerDebug; } + bool getControllerAbortOnWarning() const { + return m_controllerAbortOnWarning; + } bool getDeveloper() const { return m_developer; } bool getSafeMode() const { return m_safeMode; } bool useColors() const { @@ -73,6 +76,7 @@ class CmdlineArgs final { QList m_musicFiles; // List of files to load into players at startup bool m_startInFullscreen; // Start in fullscreen mode bool m_controllerDebug; + bool m_controllerAbortOnWarning; // Controller Engine will be stricter bool m_developer; // Developer Mode bool m_safeMode; bool m_useVuMeterGL;