Qml Debugger: Support "evaluate" while engine is running

Newer versions of Qt can evaluate expressions without pausing the
QML engine. We can take advantage of that. At the same time we can
remove the crutch of using QQmlEngineDebugService for this. The
latter produces inconsistent and generally worse results than the V4
debug service.

Task-number: QTCREATORBUG-14931
Change-Id: Ic78d08a0b00cf7de3911b7b672ce229c6d779363
Reviewed-by: hjk <hjk@theqtcompany.com>
This commit is contained in:
Ulf Hermann
2015-11-30 14:15:54 +01:00
parent 33c0b69326
commit 1d7243e724
5 changed files with 22 additions and 43 deletions

View File

@@ -49,7 +49,7 @@ namespace Utils { class SavedAction; }
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
typedef std::function<bool(QString)> ScriptEvaluator; typedef std::function<void(QString)> ScriptEvaluator;
class ConsoleItemModel; class ConsoleItemModel;
class ConsoleView; class ConsoleView;

View File

@@ -1743,12 +1743,6 @@ void DebuggerEngine::executeDebuggerCommand(const QString &, DebuggerLanguages)
showStatusMessage(tr("This debugger cannot handle user input.")); showStatusMessage(tr("This debugger cannot handle user input."));
} }
bool DebuggerEngine::evaluateScript(const QString &)
{
showStatusMessage(tr("This debugger cannot handle user scripts."));
return false;
}
BreakHandler *DebuggerEngine::breakHandler() const BreakHandler *DebuggerEngine::breakHandler() const
{ {
return Internal::breakHandler(); return Internal::breakHandler();

View File

@@ -262,7 +262,6 @@ public:
virtual bool acceptsDebuggerCommands() const { return true; } virtual bool acceptsDebuggerCommands() const { return true; }
virtual void executeDebuggerCommand(const QString &command, DebuggerLanguages languages); virtual void executeDebuggerCommand(const QString &command, DebuggerLanguages languages);
virtual bool evaluateScript(const QString &expression);
virtual void assignValueInDebugger(WatchItem *item, virtual void assignValueInDebugger(WatchItem *item,
const QString &expr, const QVariant &value); const QString &expr, const QVariant &value);

View File

@@ -213,6 +213,7 @@ public:
QList<quint32> queryIds; QList<quint32> queryIds;
bool retryOnConnectFail = false; bool retryOnConnectFail = false;
bool automaticConnect = false; bool automaticConnect = false;
bool unpausedEvaluate = false;
QTimer connectionTimer; QTimer connectionTimer;
QmlDebug::QmlDebugConnection *connection; QmlDebug::QmlDebugConnection *connection;
@@ -282,8 +283,8 @@ QmlEngine::QmlEngine(const DebuggerRunParameters &startParameters, DebuggerEngin
d->automaticConnect = true; d->automaticConnect = true;
} }
debuggerConsole()->setScriptEvaluator([this](const QString &expr) -> bool { debuggerConsole()->setScriptEvaluator([this](const QString &expr) {
return evaluateScript(expr); executeDebuggerCommand(expr, QmlLanguage);
}); });
d->connectionTimer.setInterval(4000); d->connectionTimer.setInterval(4000);
@@ -1116,35 +1117,21 @@ void QmlEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages
if (!(languages & QmlLanguage)) if (!(languages & QmlLanguage))
return; return;
if (state() == InferiorStopOk) {
StackHandler *handler = stackHandler(); StackHandler *handler = stackHandler();
if (handler->isContentsValid() && handler->currentFrame().isUsable()) { if (handler->isContentsValid() && handler->currentFrame().isUsable()) {
d->evaluate(command, CB(d->handleExecuteDebuggerCommand)); d->evaluate(command, CB(d->handleExecuteDebuggerCommand));
} else { } else {
//Currently cannot evaluate if not in a javascript break // Paused but no stack? Something is wrong
d->engine->showMessage(QString(_("Cannot evaluate %1 in current stack frame")).arg( d->engine->showMessage(_("Cannot evaluate %1. The stack trace is broken.").arg(command),
command), ConsoleOutput); ConsoleOutput);
} }
} } else if (d->unpausedEvaluate) {
d->evaluate(command, CB(d->handleExecuteDebuggerCommand));
bool QmlEngine::evaluateScript(const QString &expression)
{
bool didEvaluate = true;
// Evaluate expression based on engine state
// When engine->state() == InferiorStopOk, the expression is sent to debuggerClient.
if (state() != InferiorStopOk) {
QModelIndex currentIndex = inspectorView()->currentIndex();
quint32 queryId = d->inspectorAgent.queryExpressionResult
(watchHandler()->watchItem(currentIndex)->id, expression);
if (queryId) {
d->queryIds.append(queryId);
} else { } else {
didEvaluate = false; d->engine->showMessage(_("The application has to be paused in order to evaluate "
debuggerConsole()->printItem(ConsoleItem::ErrorType, _("Error evaluating expression.")); "expressions").arg(command), ConsoleOutput);
} }
} else {
executeDebuggerCommand(expression, QmlLanguage);
}
return didEvaluate;
} }
void QmlEnginePrivate::updateScriptSource(const QString &fileName, int lineOffset, int columnOffset, void QmlEnginePrivate::updateScriptSource(const QString &fileName, int lineOffset, int columnOffset,
@@ -1330,12 +1317,14 @@ void QmlEnginePrivate::evaluate(const QString expr, const QmlCallback &cb)
// The Qt side Q_ASSERTs otherwise. So ignore the request and hope // The Qt side Q_ASSERTs otherwise. So ignore the request and hope
// it will be repeated soon enough (which it will, e.g. in updateLocals) // it will be repeated soon enough (which it will, e.g. in updateLocals)
QTC_ASSERT(engine->state() == InferiorStopOk, return); QTC_ASSERT(unpausedEvaluate || engine->state() == InferiorStopOk, return);
DebuggerCommand cmd(EVALUATE); DebuggerCommand cmd(EVALUATE);
cmd.arg(EXPRESSION, expr); cmd.arg(EXPRESSION, expr);
cmd.arg(FRAME, engine->stackHandler()->currentIndex()); StackHandler *handler = engine->stackHandler();
if (handler->currentFrame().isUsable())
cmd.arg(FRAME, handler->currentIndex());
runCommand(cmd, cb); runCommand(cmd, cb);
} }
@@ -2497,15 +2486,13 @@ void QmlEnginePrivate::stateChanged(State state)
/// Start session. /// Start session.
flushSendBuffer(); flushSendBuffer();
runDirectCommand(CONNECT); runDirectCommand(CONNECT);
runCommand({VERSION}); // Only used for logging. runCommand({VERSION}, CB(handleVersion));
} }
} }
void QmlEnginePrivate::handleVersion(const QVariantMap &response) void QmlEnginePrivate::handleVersion(const QVariantMap &response)
{ {
engine->showMessage(QString(_("Using V8 Version: %1")).arg( unpausedEvaluate = response.value(_(BODY)).toMap().value(_("UnpausedEvaluate"), false).toBool();
response.value(_(BODY)).toMap().
value(_("V8Version")).toString()), LogOutput);
} }
void QmlEnginePrivate::flushSendBuffer() void QmlEnginePrivate::flushSendBuffer()

View File

@@ -130,7 +130,6 @@ private:
void expandItem(const QByteArray &iname) override; void expandItem(const QByteArray &iname) override;
void selectWatchData(const QByteArray &iname) override; void selectWatchData(const QByteArray &iname) override;
void executeDebuggerCommand(const QString &command, DebuggerLanguages languages) override; void executeDebuggerCommand(const QString &command, DebuggerLanguages languages) override;
bool evaluateScript(const QString &expression) override;
bool hasCapability(unsigned) const override; bool hasCapability(unsigned) const override;
void quitDebugger() override; void quitDebugger() override;