2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
2022-12-21 10:12:09 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2010-06-09 16:13:47 +02:00
|
|
|
|
|
|
|
|
#include "qmlengine.h"
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
#include "interactiveinterpreter.h"
|
2012-04-18 14:20:54 +02:00
|
|
|
#include "qmlinspectoragent.h"
|
2015-07-08 13:14:03 +02:00
|
|
|
#include "qmlv8debuggerclientconstants.h"
|
|
|
|
|
#include "qmlengineutils.h"
|
2010-06-09 16:13:47 +02:00
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
#include <debugger/breakhandler.h>
|
2022-07-05 15:37:08 +02:00
|
|
|
#include <debugger/console/console.h>
|
2013-08-29 16:36:42 +02:00
|
|
|
#include <debugger/debuggeractions.h>
|
|
|
|
|
#include <debugger/debuggercore.h>
|
|
|
|
|
#include <debugger/debuggerinternalconstants.h>
|
|
|
|
|
#include <debugger/debuggertooltipmanager.h>
|
2022-07-05 15:37:08 +02:00
|
|
|
#include <debugger/debuggertr.h>
|
2015-07-08 13:14:03 +02:00
|
|
|
#include <debugger/sourcefileshandler.h>
|
|
|
|
|
#include <debugger/stackhandler.h>
|
2015-02-26 17:19:57 +01:00
|
|
|
#include <debugger/threaddata.h>
|
2015-07-08 13:14:03 +02:00
|
|
|
#include <debugger/watchhandler.h>
|
2013-08-29 16:36:42 +02:00
|
|
|
#include <debugger/watchwindow.h>
|
2010-06-16 11:08:54 +02:00
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
|
|
|
|
#include <coreplugin/helpmanager.h>
|
|
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
|
|
2022-06-14 11:34:02 +02:00
|
|
|
#include <projectexplorer/projectnodes.h>
|
|
|
|
|
#include <projectexplorer/projecttree.h>
|
|
|
|
|
|
2011-11-30 15:23:02 +01:00
|
|
|
#include <qmljseditor/qmljseditorconstants.h>
|
2012-02-28 17:30:15 +01:00
|
|
|
#include <qmljs/qmljsmodelmanagerinterface.h>
|
2017-09-28 11:32:42 +02:00
|
|
|
#include <qmldebug/qmldebugconnection.h>
|
2015-11-16 16:46:51 +01:00
|
|
|
#include <qmldebug/qpacketprotocol.h>
|
2010-07-01 11:48:43 +02:00
|
|
|
|
2014-09-26 09:14:03 +02:00
|
|
|
#include <texteditor/textdocument.h>
|
|
|
|
|
#include <texteditor/texteditor.h>
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2017-08-29 11:48:48 +02:00
|
|
|
#include <app/app_version.h>
|
2022-05-04 16:30:55 +02:00
|
|
|
|
2016-04-19 22:49:23 +02:00
|
|
|
#include <utils/basetreeview.h>
|
2019-08-26 18:42:51 +02:00
|
|
|
#include <utils/fileinprojectfinder.h>
|
2023-05-03 17:05:35 +02:00
|
|
|
#include <utils/process.h>
|
2015-07-08 13:14:03 +02:00
|
|
|
#include <utils/qtcassert.h>
|
2022-05-04 16:30:55 +02:00
|
|
|
#include <utils/treemodel.h>
|
2011-11-30 15:23:02 +01:00
|
|
|
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QDebug>
|
|
|
|
|
#include <QDir>
|
2014-10-17 13:40:04 +02:00
|
|
|
#include <QDockWidget>
|
2015-07-08 13:14:03 +02:00
|
|
|
#include <QFileInfo>
|
2017-09-12 10:53:56 +02:00
|
|
|
#include <QHostAddress>
|
2015-07-08 13:14:03 +02:00
|
|
|
#include <QJsonArray>
|
|
|
|
|
#include <QJsonDocument>
|
|
|
|
|
#include <QJsonObject>
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QMessageBox>
|
|
|
|
|
#include <QPlainTextEdit>
|
2015-07-08 13:14:03 +02:00
|
|
|
#include <QTimer>
|
2010-07-01 11:48:43 +02:00
|
|
|
|
2016-11-29 13:22:23 +01:00
|
|
|
#include <memory.h>
|
|
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
#define DEBUG_QML 0
|
2010-06-09 16:13:47 +02:00
|
|
|
#if DEBUG_QML
|
|
|
|
|
# define SDEBUG(s) qDebug() << s
|
|
|
|
|
#else
|
|
|
|
|
# define SDEBUG(s)
|
|
|
|
|
#endif
|
|
|
|
|
# define XSDEBUG(s) qDebug() << s
|
|
|
|
|
|
2015-07-14 16:24:26 +02:00
|
|
|
#define CB(callback) [this](const QVariantMap &r) { callback(r); }
|
2017-11-07 17:55:05 +01:00
|
|
|
#define CHECK_STATE(s) do { checkState(s, __FILE__, __LINE__); } while (0)
|
2015-07-14 16:24:26 +02:00
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
using namespace Core;
|
|
|
|
|
using namespace ProjectExplorer;
|
|
|
|
|
using namespace QmlDebug;
|
2012-02-28 17:30:15 +01:00
|
|
|
using namespace QmlJS;
|
2015-07-08 13:14:03 +02:00
|
|
|
using namespace TextEditor;
|
2015-07-15 17:49:04 +02:00
|
|
|
using namespace Utils;
|
2011-01-12 14:26:50 +01:00
|
|
|
|
2022-07-05 15:37:08 +02:00
|
|
|
namespace Debugger::Internal {
|
2010-06-09 16:13:47 +02:00
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
enum Exceptions
|
2014-06-04 14:45:10 +02:00
|
|
|
{
|
2015-07-08 13:14:03 +02:00
|
|
|
NoExceptions,
|
|
|
|
|
UncaughtExceptions,
|
|
|
|
|
AllExceptions
|
|
|
|
|
};
|
2014-06-04 14:45:10 +02:00
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
enum StepAction
|
2012-02-28 17:30:15 +01:00
|
|
|
{
|
2015-07-08 13:14:03 +02:00
|
|
|
Continue,
|
|
|
|
|
StepIn,
|
|
|
|
|
StepOut,
|
|
|
|
|
Next
|
|
|
|
|
};
|
2012-02-28 17:30:15 +01:00
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
struct QmlV8ObjectData
|
|
|
|
|
{
|
2016-04-13 14:39:18 +02:00
|
|
|
int handle = -1;
|
2017-03-09 16:17:38 +01:00
|
|
|
int expectedProperties = -1;
|
2016-06-07 17:04:53 +02:00
|
|
|
QString name;
|
|
|
|
|
QString type;
|
2015-07-08 13:14:03 +02:00
|
|
|
QVariant value;
|
|
|
|
|
QVariantList properties;
|
2017-03-09 16:17:38 +01:00
|
|
|
|
|
|
|
|
bool hasChildren() const
|
|
|
|
|
{
|
|
|
|
|
return expectedProperties > 0 || !properties.isEmpty();
|
|
|
|
|
}
|
2015-07-08 13:14:03 +02:00
|
|
|
};
|
2012-02-28 17:30:15 +01:00
|
|
|
|
2018-07-23 22:28:49 +02:00
|
|
|
using QmlCallback = std::function<void(const QVariantMap &)>;
|
2015-07-14 16:24:26 +02:00
|
|
|
|
2015-07-14 16:59:46 +02:00
|
|
|
struct LookupData
|
|
|
|
|
{
|
2016-06-07 17:04:53 +02:00
|
|
|
QString iname;
|
2015-07-14 16:59:46 +02:00
|
|
|
QString name;
|
2016-06-07 17:04:53 +02:00
|
|
|
QString exp;
|
2015-07-14 16:59:46 +02:00
|
|
|
};
|
|
|
|
|
|
2018-07-23 22:28:49 +02:00
|
|
|
using LookupItems = QHash<int, LookupData>; // id -> (iname, exp)
|
2015-07-14 16:59:46 +02:00
|
|
|
|
2018-07-09 14:58:44 +02:00
|
|
|
static void setWatchItemHasChildren(WatchItem *item, bool hasChildren)
|
|
|
|
|
{
|
|
|
|
|
item->setHasChildren(hasChildren);
|
|
|
|
|
item->valueEditable = !hasChildren;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-11 09:15:50 +02:00
|
|
|
class QmlEnginePrivate : public QmlDebugClient
|
2015-07-08 13:14:03 +02:00
|
|
|
{
|
|
|
|
|
public:
|
2017-09-07 15:17:15 +02:00
|
|
|
QmlEnginePrivate(QmlEngine *engine_, QmlDebugConnection *connection)
|
|
|
|
|
: QmlDebugClient("V8Debugger", connection),
|
2015-07-08 13:14:03 +02:00
|
|
|
engine(engine_),
|
2017-09-07 15:17:15 +02:00
|
|
|
inspectorAgent(engine, connection)
|
2015-07-08 13:14:03 +02:00
|
|
|
{}
|
|
|
|
|
|
2017-09-07 15:20:29 +02:00
|
|
|
void messageReceived(const QByteArray &data) override;
|
|
|
|
|
void stateChanged(State state) override;
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
void continueDebugging(StepAction stepAction);
|
|
|
|
|
|
2016-12-07 16:43:12 +01:00
|
|
|
void evaluate(const QString expr, qint64 context, const QmlCallback &cb);
|
2015-07-14 16:59:46 +02:00
|
|
|
void lookup(const LookupItems &items);
|
2015-07-14 16:24:26 +02:00
|
|
|
void backtrace();
|
2015-07-14 16:59:46 +02:00
|
|
|
void updateLocals();
|
2015-07-10 11:06:27 +02:00
|
|
|
void scope(int number, int frameNumber = -1);
|
2015-07-08 13:14:03 +02:00
|
|
|
void scripts(int types = 4, const QList<int> ids = QList<int>(),
|
|
|
|
|
bool includeSource = false, const QVariant filter = QVariant());
|
|
|
|
|
|
|
|
|
|
void setBreakpoint(const QString type, const QString target,
|
|
|
|
|
bool enabled = true,int line = 0, int column = 0,
|
|
|
|
|
const QString condition = QString(), int ignoreCount = -1);
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
void clearBreakpoint(const Breakpoint &bp);
|
2018-07-18 15:41:09 +02:00
|
|
|
|
|
|
|
|
bool canChangeBreakpoint() const;
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
void changeBreakpoint(const Breakpoint &bp, bool enabled);
|
2018-07-18 15:41:09 +02:00
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
void setExceptionBreak(Exceptions type, bool enabled = false);
|
|
|
|
|
|
|
|
|
|
void flushSendBuffer();
|
|
|
|
|
|
2015-07-14 16:24:26 +02:00
|
|
|
void handleBacktrace(const QVariantMap &response);
|
|
|
|
|
void handleLookup(const QVariantMap &response);
|
2015-07-14 16:59:46 +02:00
|
|
|
void handleExecuteDebuggerCommand(const QVariantMap &response);
|
2016-06-07 17:04:53 +02:00
|
|
|
void handleEvaluateExpression(const QVariantMap &response, const QString &iname, const QString &expr);
|
2015-07-14 16:24:26 +02:00
|
|
|
void handleFrame(const QVariantMap &response);
|
|
|
|
|
void handleScope(const QVariantMap &response);
|
|
|
|
|
void handleVersion(const QVariantMap &response);
|
2015-07-13 13:36:28 +02:00
|
|
|
StackFrame extractStackFrame(const QVariant &bodyVal);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
bool canEvaluateScript(const QString &script);
|
|
|
|
|
void updateScriptSource(const QString &fileName, int lineOffset, int columnOffset, const QString &source);
|
2015-07-10 13:06:00 +02:00
|
|
|
|
2015-07-14 16:24:26 +02:00
|
|
|
void runCommand(const DebuggerCommand &command, const QmlCallback &cb = QmlCallback());
|
2016-06-07 17:04:53 +02:00
|
|
|
void runDirectCommand(const QString &type, const QByteArray &msg = QByteArray());
|
2012-02-28 17:30:15 +01:00
|
|
|
|
2015-07-13 13:36:28 +02:00
|
|
|
void clearRefs() { refVals.clear(); }
|
|
|
|
|
void memorizeRefs(const QVariant &refs);
|
|
|
|
|
QmlV8ObjectData extractData(const QVariant &data) const;
|
|
|
|
|
void insertSubItems(WatchItem *parent, const QVariantList &properties);
|
2015-07-14 16:59:46 +02:00
|
|
|
void checkForFinishedUpdate();
|
2015-08-21 17:33:53 +02:00
|
|
|
ConsoleItem *constructLogItemTree(const QmlV8ObjectData &objectData);
|
2015-07-13 13:36:28 +02:00
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
public:
|
2015-07-13 13:36:28 +02:00
|
|
|
QHash<int, QmlV8ObjectData> refVals; // The mapping of target object handles to retrieved values.
|
2015-07-08 13:14:03 +02:00
|
|
|
int sequence = -1;
|
|
|
|
|
QmlEngine *engine;
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
QHash<int, Breakpoint> breakpointsSync;
|
|
|
|
|
QList<QString> breakpointsTemp;
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2015-07-14 16:59:46 +02:00
|
|
|
LookupItems currentlyLookingUp; // Id -> inames
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
//Cache
|
|
|
|
|
QList<int> currentFrameScopes;
|
|
|
|
|
QHash<int, int> stackIndexLookup;
|
|
|
|
|
|
|
|
|
|
StepAction previousStepAction = Continue;
|
|
|
|
|
|
|
|
|
|
QList<QByteArray> sendBuffer;
|
|
|
|
|
|
|
|
|
|
QHash<QString, QTextDocument*> sourceDocuments;
|
|
|
|
|
InteractiveInterpreter interpreter;
|
2023-05-03 16:00:22 +02:00
|
|
|
Process process;
|
2015-08-26 11:51:56 +02:00
|
|
|
QmlInspectorAgent inspectorAgent;
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
QList<quint32> queryIds;
|
|
|
|
|
bool retryOnConnectFail = false;
|
|
|
|
|
bool automaticConnect = false;
|
2015-11-30 14:15:54 +01:00
|
|
|
bool unpausedEvaluate = false;
|
2016-12-09 12:51:50 +01:00
|
|
|
bool contextEvaluate = false;
|
2018-07-18 15:41:09 +02:00
|
|
|
bool supportChangeBreakpoint = false;
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
QTimer connectionTimer;
|
2018-02-01 10:59:24 +01:00
|
|
|
QmlDebug::QDebugMessageClient *msgClient = nullptr;
|
2015-07-14 16:24:26 +02:00
|
|
|
|
|
|
|
|
QHash<int, QmlCallback> callbackForToken;
|
2017-04-11 09:15:50 +02:00
|
|
|
|
2019-08-26 18:42:51 +02:00
|
|
|
FileInProjectFinder fileFinder;
|
|
|
|
|
|
2015-08-21 17:33:53 +02:00
|
|
|
private:
|
|
|
|
|
ConsoleItem *constructLogItemTree(const QmlV8ObjectData &objectData, QList<int> &seenHandles);
|
|
|
|
|
void constructChildLogItems(ConsoleItem *item, const QmlV8ObjectData &objectData,
|
|
|
|
|
QList<int> &seenHandles);
|
2012-02-28 17:30:15 +01:00
|
|
|
};
|
2010-09-13 13:30:35 +02:00
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
static void updateDocument(IDocument *document, const QTextDocument *textDocument)
|
2012-10-08 13:17:10 +02:00
|
|
|
{
|
2015-07-08 13:14:03 +02:00
|
|
|
if (auto baseTextDocument = qobject_cast<TextDocument *>(document))
|
|
|
|
|
baseTextDocument->document()->setPlainText(textDocument->toPlainText());
|
2012-10-08 13:17:10 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2010-06-09 16:13:47 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// QmlEngine
|
|
|
|
|
//
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2017-09-26 16:24:05 +02:00
|
|
|
QmlEngine::QmlEngine()
|
2017-05-05 14:45:36 +02:00
|
|
|
: d(new QmlEnginePrivate(this, new QmlDebugConnection(this)))
|
2010-06-09 16:13:47 +02:00
|
|
|
{
|
2016-06-07 17:04:53 +02:00
|
|
|
setObjectName("QmlEngine");
|
2018-09-04 13:29:15 +02:00
|
|
|
setDebuggerName("QML");
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
|
2017-09-07 15:17:15 +02:00
|
|
|
QmlDebugConnection *connection = d->connection();
|
2011-07-28 17:04:35 +02:00
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
connect(stackHandler(), &StackHandler::stackChanged,
|
|
|
|
|
this, &QmlEngine::updateCurrentContext);
|
|
|
|
|
connect(stackHandler(), &StackHandler::currentIndexChanged,
|
|
|
|
|
this, &QmlEngine::updateCurrentContext);
|
|
|
|
|
|
2023-05-03 16:00:22 +02:00
|
|
|
connect(&d->process, &Process::readyReadStandardOutput, this, [this] {
|
2022-05-04 16:30:55 +02:00
|
|
|
// FIXME: Redirect to RunControl
|
2023-01-05 17:55:04 +01:00
|
|
|
showMessage(d->process.readAllStandardOutput(), AppOutput);
|
2022-05-04 16:30:55 +02:00
|
|
|
});
|
2023-05-03 16:00:22 +02:00
|
|
|
connect(&d->process, &Process::readyReadStandardError, this, [this] {
|
2022-05-04 16:30:55 +02:00
|
|
|
// FIXME: Redirect to RunControl
|
2023-01-05 17:55:04 +01:00
|
|
|
showMessage(d->process.readAllStandardError(), AppOutput);
|
2022-05-04 16:30:55 +02:00
|
|
|
});
|
|
|
|
|
|
2023-05-03 16:00:22 +02:00
|
|
|
connect(&d->process, &Process::done, this, &QmlEngine::disconnected);
|
|
|
|
|
connect(&d->process, &Process::started, this, &QmlEngine::handleLauncherStarted);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2018-06-12 10:44:10 +02:00
|
|
|
debuggerConsole()->populateFileFinder();
|
2015-11-30 14:15:54 +01:00
|
|
|
debuggerConsole()->setScriptEvaluator([this](const QString &expr) {
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
executeDebuggerCommand(expr);
|
2015-11-10 16:59:02 +01:00
|
|
|
});
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
d->connectionTimer.setInterval(4000);
|
|
|
|
|
d->connectionTimer.setSingleShot(true);
|
|
|
|
|
connect(&d->connectionTimer, &QTimer::timeout,
|
|
|
|
|
this, &QmlEngine::checkConnectionState);
|
|
|
|
|
|
2017-09-07 15:17:15 +02:00
|
|
|
connect(connection, &QmlDebugConnection::logStateChange,
|
2016-07-15 13:09:38 +02:00
|
|
|
this, &QmlEngine::showConnectionStateMessage);
|
2017-09-07 15:17:15 +02:00
|
|
|
connect(connection, &QmlDebugConnection::logError, this,
|
2017-03-16 15:04:58 +01:00
|
|
|
[this](const QString &error) { showMessage("QML Debugger: " + error, LogWarning); });
|
2016-07-15 13:09:38 +02:00
|
|
|
|
2017-09-07 15:17:15 +02:00
|
|
|
connect(connection, &QmlDebugConnection::connectionFailed,
|
2016-07-15 13:09:38 +02:00
|
|
|
this, &QmlEngine::connectionFailed);
|
2017-09-07 15:17:15 +02:00
|
|
|
connect(connection, &QmlDebugConnection::connected,
|
2015-07-08 13:14:03 +02:00
|
|
|
&d->connectionTimer, &QTimer::stop);
|
2017-09-07 15:17:15 +02:00
|
|
|
connect(connection, &QmlDebugConnection::connected,
|
2015-07-08 13:14:03 +02:00
|
|
|
this, &QmlEngine::connectionEstablished);
|
2017-09-07 15:17:15 +02:00
|
|
|
connect(connection, &QmlDebugConnection::disconnected,
|
2015-07-08 13:14:03 +02:00
|
|
|
this, &QmlEngine::disconnected);
|
|
|
|
|
|
2017-09-07 15:17:15 +02:00
|
|
|
d->msgClient = new QDebugMessageClient(connection);
|
2015-07-08 13:14:03 +02:00
|
|
|
connect(d->msgClient, &QDebugMessageClient::newState,
|
2016-04-13 11:15:06 +02:00
|
|
|
this, [this](QmlDebugClient::State state) {
|
|
|
|
|
logServiceStateChange(d->msgClient->name(), d->msgClient->serviceVersion(), state);
|
|
|
|
|
});
|
|
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
connect(d->msgClient, &QDebugMessageClient::message,
|
|
|
|
|
this, &appendDebugOutput);
|
2010-06-09 16:13:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QmlEngine::~QmlEngine()
|
2011-07-06 19:07:38 +02:00
|
|
|
{
|
2015-07-08 13:14:03 +02:00
|
|
|
delete d;
|
2011-07-06 19:07:38 +02:00
|
|
|
}
|
2010-08-18 13:54:12 +02:00
|
|
|
|
2017-05-19 13:14:44 +02:00
|
|
|
void QmlEngine::setState(DebuggerState state, bool forced)
|
|
|
|
|
{
|
|
|
|
|
DebuggerEngine::setState(state, forced);
|
|
|
|
|
updateCurrentContext();
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-06 15:04:25 +01:00
|
|
|
void QmlEngine::handleLauncherStarted()
|
|
|
|
|
{
|
|
|
|
|
// FIXME: The QmlEngine never calls notifyInferiorPid() triggering the
|
|
|
|
|
// raising, so do it here manually for now.
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
ProcessHandle(inferiorPid()).activate();
|
2017-08-10 19:03:07 +02:00
|
|
|
tryToConnect();
|
2017-03-06 15:04:25 +01:00
|
|
|
}
|
|
|
|
|
|
2010-08-13 14:18:10 +02:00
|
|
|
void QmlEngine::connectionEstablished()
|
|
|
|
|
{
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
connect(inspectorView(), &WatchTreeView::currentIndexChanged,
|
|
|
|
|
this, &QmlEngine::updateCurrentContext);
|
|
|
|
|
|
2011-10-19 17:49:24 +02:00
|
|
|
if (state() == EngineRunRequested)
|
|
|
|
|
notifyEngineRunAndInferiorRunOk();
|
2010-08-13 14:18:10 +02:00
|
|
|
}
|
2010-11-15 17:09:28 +01:00
|
|
|
|
2017-08-10 19:03:07 +02:00
|
|
|
void QmlEngine::tryToConnect()
|
2012-03-14 15:28:01 +01:00
|
|
|
{
|
2017-08-10 19:03:07 +02:00
|
|
|
showMessage("QML Debugger: Trying to connect ...", LogStatus);
|
2015-07-08 13:14:03 +02:00
|
|
|
d->retryOnConnectFail = true;
|
2012-06-12 10:48:54 +02:00
|
|
|
if (state() == EngineRunRequested) {
|
2017-12-14 10:12:17 +01:00
|
|
|
if (isDying()) {
|
2012-06-12 10:48:54 +02:00
|
|
|
// Probably cpp is being debugged and hence we did not get the output yet.
|
2022-07-05 15:37:08 +02:00
|
|
|
appStartupFailed(Tr::tr("No application output received in time"));
|
2012-06-12 10:48:54 +02:00
|
|
|
} else {
|
2017-08-10 19:03:07 +02:00
|
|
|
beginConnection();
|
2012-06-12 10:48:54 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
2015-07-08 13:14:03 +02:00
|
|
|
d->automaticConnect = true;
|
2012-06-12 10:48:54 +02:00
|
|
|
}
|
2012-03-14 15:28:01 +01:00
|
|
|
}
|
|
|
|
|
|
2017-08-10 19:03:07 +02:00
|
|
|
void QmlEngine::beginConnection()
|
2011-07-28 10:38:39 +02:00
|
|
|
{
|
2015-07-08 13:14:03 +02:00
|
|
|
if (state() != EngineRunRequested && d->retryOnConnectFail)
|
2012-03-22 13:00:56 +01:00
|
|
|
return;
|
|
|
|
|
|
2012-04-17 08:01:25 +02:00
|
|
|
QTC_ASSERT(state() == EngineRunRequested, return);
|
2012-03-22 13:00:56 +01:00
|
|
|
|
2017-05-02 15:03:56 +02:00
|
|
|
|
2017-08-18 12:23:42 +02:00
|
|
|
QString host = runParameters().qmlServer.host();
|
2012-11-06 15:46:19 +01:00
|
|
|
// Use localhost as default
|
|
|
|
|
if (host.isEmpty())
|
2017-08-15 13:38:09 +02:00
|
|
|
host = QHostAddress(QHostAddress::LocalHost).toString();
|
2012-11-06 15:46:19 +01:00
|
|
|
|
2017-08-10 19:03:07 +02:00
|
|
|
// FIXME: Not needed?
|
2013-07-11 12:04:34 +02:00
|
|
|
/*
|
|
|
|
|
* Let plugin-specific code override the port printed by the application. This is necessary
|
|
|
|
|
* in the case of port forwarding, when the port the application listens on is not the same that
|
|
|
|
|
* we want to connect to.
|
|
|
|
|
* NOTE: It is still necessary to wait for the output in that case, because otherwise we cannot
|
|
|
|
|
* be sure that the port is already open. The usual method of trying to connect repeatedly
|
|
|
|
|
* will not work, because the intermediate port is already open. So the connection
|
|
|
|
|
* will be accepted on that port but the forwarding to the target port will fail and
|
|
|
|
|
* the connection will be closed again (instead of returning the "connection refused"
|
|
|
|
|
* error that we expect).
|
|
|
|
|
*/
|
2017-08-18 12:23:42 +02:00
|
|
|
int port = runParameters().qmlServer.port();
|
2013-07-11 12:04:34 +02:00
|
|
|
|
2017-09-07 15:17:15 +02:00
|
|
|
QmlDebugConnection *connection = d->connection();
|
|
|
|
|
if (!connection || connection->isConnected())
|
2015-07-08 13:14:03 +02:00
|
|
|
return;
|
2010-08-13 14:18:10 +02:00
|
|
|
|
2017-09-07 15:17:15 +02:00
|
|
|
connection->connectToHost(host, port);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
//A timeout to check the connection state
|
|
|
|
|
d->connectionTimer.start();
|
|
|
|
|
}
|
2013-07-11 12:04:34 +02:00
|
|
|
|
2012-03-14 15:33:33 +01:00
|
|
|
void QmlEngine::connectionStartupFailed()
|
2010-08-13 14:18:10 +02:00
|
|
|
{
|
2015-07-08 13:14:03 +02:00
|
|
|
if (d->retryOnConnectFail) {
|
2012-03-16 12:55:00 +01:00
|
|
|
// retry after 3 seconds ...
|
2016-04-19 22:49:23 +02:00
|
|
|
QTimer::singleShot(3000, this, [this] { beginConnection(); });
|
2012-03-14 15:28:01 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2011-10-13 15:32:42 +02:00
|
|
|
|
2020-06-02 09:10:40 +02:00
|
|
|
auto infoBox = new QMessageBox(ICore::dialogParent());
|
2011-02-24 10:03:44 +01:00
|
|
|
infoBox->setIcon(QMessageBox::Critical);
|
2017-08-29 11:48:48 +02:00
|
|
|
infoBox->setWindowTitle(Core::Constants::IDE_DISPLAY_NAME);
|
2022-07-05 15:37:08 +02:00
|
|
|
infoBox->setText(Tr::tr("Could not connect to the in-process QML debugger."
|
2012-03-14 15:33:33 +01:00
|
|
|
"\nDo you want to retry?"));
|
|
|
|
|
infoBox->setStandardButtons(QMessageBox::Retry | QMessageBox::Cancel |
|
|
|
|
|
QMessageBox::Help);
|
|
|
|
|
infoBox->setDefaultButton(QMessageBox::Retry);
|
2011-02-24 10:03:44 +01:00
|
|
|
infoBox->setModal(true);
|
|
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
connect(infoBox, &QDialog::finished,
|
|
|
|
|
this, &QmlEngine::errorMessageBoxFinished);
|
2011-02-24 10:03:44 +01:00
|
|
|
|
|
|
|
|
infoBox->show();
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-14 15:33:33 +01:00
|
|
|
void QmlEngine::appStartupFailed(const QString &errorMessage)
|
|
|
|
|
{
|
2022-07-05 15:37:08 +02:00
|
|
|
QString error = Tr::tr("Could not connect to the in-process QML debugger. %1").arg(errorMessage);
|
2014-03-18 12:55:26 +01:00
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
if (companionEngine()) {
|
2020-06-02 09:10:40 +02:00
|
|
|
auto infoBox = new QMessageBox(ICore::dialogParent());
|
2014-03-18 12:55:26 +01:00
|
|
|
infoBox->setIcon(QMessageBox::Critical);
|
2017-08-29 11:48:48 +02:00
|
|
|
infoBox->setWindowTitle(Core::Constants::IDE_DISPLAY_NAME);
|
2014-03-18 12:55:26 +01:00
|
|
|
infoBox->setText(error);
|
|
|
|
|
infoBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Help);
|
|
|
|
|
infoBox->setDefaultButton(QMessageBox::Ok);
|
2015-07-08 13:14:03 +02:00
|
|
|
connect(infoBox, &QDialog::finished,
|
|
|
|
|
this, &QmlEngine::errorMessageBoxFinished);
|
2014-03-18 12:55:26 +01:00
|
|
|
infoBox->show();
|
|
|
|
|
} else {
|
2015-11-10 16:59:02 +01:00
|
|
|
debuggerConsole()->printItem(ConsoleItem::WarningType, error);
|
2014-03-18 12:55:26 +01:00
|
|
|
}
|
2012-03-14 15:33:33 +01:00
|
|
|
|
|
|
|
|
notifyEngineRunFailed();
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-23 13:03:22 +01:00
|
|
|
void QmlEngine::errorMessageBoxFinished(int result)
|
2011-02-24 10:03:44 +01:00
|
|
|
{
|
|
|
|
|
switch (result) {
|
2011-02-16 14:35:26 +01:00
|
|
|
case QMessageBox::Retry: {
|
2011-07-28 10:38:39 +02:00
|
|
|
beginConnection();
|
2011-02-16 14:35:26 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case QMessageBox::Help: {
|
2019-01-24 10:42:24 +01:00
|
|
|
HelpManager::showHelpUrl("qthelp://org.qt-project.qtcreator/doc/creator-debugging-qml.html");
|
2017-07-14 13:44:33 +02:00
|
|
|
Q_FALLTHROUGH();
|
2011-02-16 14:35:26 +01:00
|
|
|
}
|
|
|
|
|
default:
|
2011-10-19 17:49:24 +02:00
|
|
|
if (state() == InferiorRunOk) {
|
|
|
|
|
notifyInferiorSpontaneousStop();
|
|
|
|
|
notifyInferiorIll();
|
2012-03-14 15:33:33 +01:00
|
|
|
} else if (state() == EngineRunRequested) {
|
2012-02-23 13:03:22 +01:00
|
|
|
notifyEngineRunFailed();
|
2011-10-19 17:49:24 +02:00
|
|
|
}
|
2011-02-16 14:37:37 +01:00
|
|
|
break;
|
2011-02-16 14:35:26 +01:00
|
|
|
}
|
2010-08-13 14:18:10 +02:00
|
|
|
}
|
|
|
|
|
|
2011-11-30 15:23:02 +01:00
|
|
|
void QmlEngine::gotoLocation(const Location &location)
|
|
|
|
|
{
|
2020-01-30 14:13:53 +01:00
|
|
|
if (QUrl(location.fileName().toString()).isLocalFile()) { // create QUrl to ensure validity
|
2020-01-02 12:37:57 +01:00
|
|
|
const QString fileName = location.fileName().toString();
|
2011-11-30 15:23:02 +01:00
|
|
|
// internal file from source files -> show generated .js
|
2015-07-08 13:14:03 +02:00
|
|
|
QTC_ASSERT(d->sourceDocuments.contains(fileName), return);
|
2011-11-30 15:23:02 +01:00
|
|
|
|
2022-07-05 15:37:08 +02:00
|
|
|
QString titlePattern = Tr::tr("JS Source for %1").arg(fileName);
|
2013-07-09 12:14:33 +02:00
|
|
|
//Check if there are open documents with the same title
|
2022-04-29 12:43:37 +02:00
|
|
|
const QList<IDocument *> documents = DocumentModel::openedDocuments();
|
|
|
|
|
for (IDocument *document: documents) {
|
2013-07-09 12:14:33 +02:00
|
|
|
if (document->displayName() == titlePattern) {
|
2015-07-08 13:14:03 +02:00
|
|
|
EditorManager::activateEditorForDocument(document);
|
2013-07-09 12:14:33 +02:00
|
|
|
return;
|
2011-12-21 10:22:55 +01:00
|
|
|
}
|
|
|
|
|
}
|
2015-07-08 13:14:03 +02:00
|
|
|
IEditor *editor = EditorManager::openEditorWithContents(
|
2013-07-09 12:14:33 +02:00
|
|
|
QmlJSEditor::Constants::C_QMLJSEDITOR_ID, &titlePattern);
|
|
|
|
|
if (editor) {
|
|
|
|
|
editor->document()->setProperty(Constants::OPENED_BY_DEBUGGER, true);
|
2015-07-08 13:14:03 +02:00
|
|
|
if (auto plainTextEdit = qobject_cast<QPlainTextEdit *>(editor->widget()))
|
2013-07-09 12:14:33 +02:00
|
|
|
plainTextEdit->setReadOnly(true);
|
2015-07-08 13:14:03 +02:00
|
|
|
updateDocument(editor->document(), d->sourceDocuments.value(fileName));
|
2011-12-21 10:39:54 +01:00
|
|
|
}
|
2011-11-30 15:23:02 +01:00
|
|
|
} else {
|
|
|
|
|
DebuggerEngine::gotoLocation(location);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-15 17:09:28 +01:00
|
|
|
void QmlEngine::closeConnection()
|
|
|
|
|
{
|
2015-07-08 13:14:03 +02:00
|
|
|
if (d->connectionTimer.isActive()) {
|
|
|
|
|
d->connectionTimer.stop();
|
|
|
|
|
} else {
|
2017-09-07 15:17:15 +02:00
|
|
|
if (QmlDebugConnection *connection = d->connection())
|
|
|
|
|
connection->close();
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
2010-11-15 17:09:28 +01:00
|
|
|
}
|
|
|
|
|
|
2022-05-04 16:30:55 +02:00
|
|
|
void QmlEngine::startProcess()
|
2010-11-15 17:09:28 +01:00
|
|
|
{
|
2022-05-04 16:30:55 +02:00
|
|
|
if (d->process.isRunning())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
d->process.setCommand(runParameters().inferior.command);
|
|
|
|
|
d->process.setWorkingDirectory(runParameters().inferior.workingDirectory);
|
|
|
|
|
d->process.setEnvironment(runParameters().inferior.environment);
|
2022-07-05 15:37:08 +02:00
|
|
|
showMessage(Tr::tr("Starting %1").arg(d->process.commandLine().toUserOutput()),
|
2022-05-04 16:30:55 +02:00
|
|
|
NormalMessageFormat);
|
|
|
|
|
d->process.start();
|
2010-11-15 17:09:28 +01:00
|
|
|
}
|
2010-08-13 14:18:10 +02:00
|
|
|
|
2022-05-04 16:30:55 +02:00
|
|
|
void QmlEngine::stopProcess()
|
2010-11-15 17:09:28 +01:00
|
|
|
{
|
2022-05-04 16:30:55 +02:00
|
|
|
if (d->process.isRunning())
|
|
|
|
|
d->process.close();
|
2010-08-18 13:54:12 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-09 17:07:59 +02:00
|
|
|
void QmlEngine::shutdownInferior()
|
2010-06-09 16:13:47 +02:00
|
|
|
{
|
2017-11-07 17:55:05 +01:00
|
|
|
CHECK_STATE(InferiorShutdownRequested);
|
2015-07-08 13:14:03 +02:00
|
|
|
// End session.
|
2015-07-10 13:06:00 +02:00
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "request",
|
|
|
|
|
// "command" : "disconnect",
|
|
|
|
|
// }
|
2015-11-03 12:01:57 +01:00
|
|
|
d->runCommand({DISCONNECT});
|
2011-08-17 13:42:41 +02:00
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
resetLocation();
|
2022-05-04 16:30:55 +02:00
|
|
|
stopProcess();
|
2012-03-06 09:21:27 +01:00
|
|
|
closeConnection();
|
2011-08-17 13:42:41 +02:00
|
|
|
|
2017-12-14 09:36:41 +01:00
|
|
|
notifyInferiorShutdownFinished();
|
2010-07-09 17:07:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlEngine::shutdownEngine()
|
|
|
|
|
{
|
2015-07-08 13:14:03 +02:00
|
|
|
clearExceptionSelection();
|
2013-07-16 14:17:32 +02:00
|
|
|
|
2015-11-10 16:59:02 +01:00
|
|
|
debuggerConsole()->setScriptEvaluator(ScriptEvaluator());
|
2012-05-10 12:20:13 +02:00
|
|
|
|
|
|
|
|
// double check (ill engine?):
|
2022-05-04 16:30:55 +02:00
|
|
|
stopProcess();
|
2010-08-13 14:18:10 +02:00
|
|
|
|
2017-12-14 09:11:16 +01:00
|
|
|
notifyEngineShutdownFinished();
|
2010-06-09 16:13:47 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-08 18:10:50 +02:00
|
|
|
void QmlEngine::setupEngine()
|
2010-06-09 16:13:47 +02:00
|
|
|
{
|
2017-06-13 08:49:18 +02:00
|
|
|
notifyEngineSetupOk();
|
2017-11-15 17:12:03 +01:00
|
|
|
|
2020-12-18 17:58:05 +01:00
|
|
|
// we won't get any debug output
|
|
|
|
|
if (!terminal()) {
|
|
|
|
|
d->retryOnConnectFail = true;
|
|
|
|
|
d->automaticConnect = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
|
|
|
|
|
|
|
|
|
|
if (isPrimaryEngine()) {
|
|
|
|
|
// QML only.
|
|
|
|
|
if (runParameters().startMode == AttachToRemoteServer)
|
|
|
|
|
tryToConnect();
|
|
|
|
|
else if (runParameters().startMode == AttachToRemoteProcess)
|
|
|
|
|
beginConnection();
|
|
|
|
|
else
|
2022-05-04 16:30:55 +02:00
|
|
|
startProcess();
|
2020-12-18 17:58:05 +01:00
|
|
|
} else {
|
|
|
|
|
tryToConnect();
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-15 17:12:03 +01:00
|
|
|
if (d->automaticConnect)
|
|
|
|
|
beginConnection();
|
2010-07-01 11:48:43 +02:00
|
|
|
}
|
|
|
|
|
|
2010-06-09 16:13:47 +02:00
|
|
|
void QmlEngine::continueInferior()
|
|
|
|
|
{
|
2010-07-13 08:41:27 +02:00
|
|
|
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
2015-07-08 13:14:03 +02:00
|
|
|
clearExceptionSelection();
|
|
|
|
|
d->continueDebugging(Continue);
|
2010-07-22 15:31:11 +02:00
|
|
|
resetLocation();
|
2010-07-13 08:41:27 +02:00
|
|
|
notifyInferiorRunRequested();
|
|
|
|
|
notifyInferiorRunOk();
|
2010-06-09 16:13:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlEngine::interruptInferior()
|
|
|
|
|
{
|
2016-06-07 17:04:53 +02:00
|
|
|
showMessage(INTERRUPT, LogInput);
|
2015-07-10 13:06:00 +02:00
|
|
|
d->runDirectCommand(INTERRUPT);
|
2022-07-05 15:37:08 +02:00
|
|
|
showStatusMessage(Tr::tr("Waiting for JavaScript engine to interrupt on next statement."));
|
2010-06-09 16:13:47 +02:00
|
|
|
}
|
|
|
|
|
|
2018-10-02 12:53:07 +02:00
|
|
|
void QmlEngine::executeStepIn(bool)
|
2010-06-09 16:13:47 +02:00
|
|
|
{
|
2015-07-08 13:14:03 +02:00
|
|
|
clearExceptionSelection();
|
|
|
|
|
d->continueDebugging(StepIn);
|
2010-07-13 08:41:27 +02:00
|
|
|
notifyInferiorRunRequested();
|
|
|
|
|
notifyInferiorRunOk();
|
2010-06-09 16:13:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlEngine::executeStepOut()
|
|
|
|
|
{
|
2015-07-08 13:14:03 +02:00
|
|
|
clearExceptionSelection();
|
|
|
|
|
d->continueDebugging(StepOut);
|
2010-07-13 08:41:27 +02:00
|
|
|
notifyInferiorRunRequested();
|
|
|
|
|
notifyInferiorRunOk();
|
2010-06-09 16:13:47 +02:00
|
|
|
}
|
|
|
|
|
|
2018-10-02 12:53:07 +02:00
|
|
|
void QmlEngine::executeStepOver(bool)
|
2010-06-09 16:13:47 +02:00
|
|
|
{
|
2015-07-08 13:14:03 +02:00
|
|
|
clearExceptionSelection();
|
|
|
|
|
d->continueDebugging(Next);
|
2010-07-13 08:41:27 +02:00
|
|
|
notifyInferiorRunRequested();
|
|
|
|
|
notifyInferiorRunOk();
|
2010-06-09 16:13:47 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-23 10:16:11 +01:00
|
|
|
void QmlEngine::executeRunToLine(const ContextData &data)
|
2010-06-09 16:13:47 +02:00
|
|
|
{
|
2011-12-22 18:43:09 +01:00
|
|
|
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
2022-07-05 15:37:08 +02:00
|
|
|
showStatusMessage(Tr::tr("Run to line %1 (%2) requested...")
|
2023-06-06 15:44:59 +02:00
|
|
|
.arg(data.textPosition.line)
|
2020-01-02 11:31:14 +01:00
|
|
|
.arg(data.fileName.toString()),
|
|
|
|
|
5000);
|
2023-06-06 15:44:59 +02:00
|
|
|
d->setBreakpoint(SCRIPTREGEXP, data.fileName.toString(), true, data.textPosition.line);
|
2015-07-08 13:14:03 +02:00
|
|
|
clearExceptionSelection();
|
|
|
|
|
d->continueDebugging(Continue);
|
|
|
|
|
|
2011-12-22 18:43:09 +01:00
|
|
|
notifyInferiorRunRequested();
|
|
|
|
|
notifyInferiorRunOk();
|
2010-06-09 16:13:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlEngine::executeRunToFunction(const QString &functionName)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(functionName)
|
|
|
|
|
XSDEBUG("FIXME: QmlEngine::executeRunToFunction()");
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-23 10:16:11 +01:00
|
|
|
void QmlEngine::executeJumpToLine(const ContextData &data)
|
2010-06-09 16:13:47 +02:00
|
|
|
{
|
2011-02-23 10:16:11 +01:00
|
|
|
Q_UNUSED(data)
|
2010-06-09 16:13:47 +02:00
|
|
|
XSDEBUG("FIXME: QmlEngine::executeJumpToLine()");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlEngine::activateFrame(int index)
|
|
|
|
|
{
|
2011-10-17 15:23:42 +02:00
|
|
|
if (state() != InferiorStopOk && state() != InferiorUnrunnable)
|
|
|
|
|
return;
|
|
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
stackHandler()->setCurrentIndex(index);
|
2019-06-24 14:54:47 +02:00
|
|
|
const StackFrame &frame = stackHandler()->frameAt(index);
|
|
|
|
|
gotoLocation(frame);
|
2015-07-14 16:59:46 +02:00
|
|
|
|
|
|
|
|
d->updateLocals();
|
2010-06-09 16:13:47 +02:00
|
|
|
}
|
|
|
|
|
|
2018-08-15 17:28:00 +02:00
|
|
|
void QmlEngine::selectThread(const Thread &thread)
|
2010-06-09 16:13:47 +02:00
|
|
|
{
|
2018-08-15 17:28:00 +02:00
|
|
|
Q_UNUSED(thread)
|
2010-06-09 16:13:47 +02:00
|
|
|
}
|
|
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
void QmlEngine::insertBreakpoint(const Breakpoint &bp)
|
2011-08-17 11:47:42 +02:00
|
|
|
{
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
QTC_ASSERT(bp, return);
|
|
|
|
|
const BreakpointState state = bp->state();
|
|
|
|
|
QTC_ASSERT(state == BreakpointInsertionRequested, qDebug() << bp << this << state);
|
|
|
|
|
notifyBreakpointInsertProceeding(bp);
|
2011-08-17 11:47:42 +02:00
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
const BreakpointParameters &requested = bp->requestedParameters();
|
|
|
|
|
if (requested.type == BreakpointAtJavaScriptThrow) {
|
|
|
|
|
bp->setPending(false);
|
|
|
|
|
notifyBreakpointInsertOk(bp);
|
|
|
|
|
d->setExceptionBreak(AllExceptions, requested.enabled);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
} else if (requested.type == BreakpointByFileAndLine) {
|
2020-01-02 11:31:14 +01:00
|
|
|
d->setBreakpoint(SCRIPTREGEXP, requested.fileName.toString(),
|
2023-06-06 15:44:59 +02:00
|
|
|
requested.enabled, requested.textPosition.line, 0,
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
requested.condition, requested.ignoreCount);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
} else if (requested.type == BreakpointOnQmlSignalEmit) {
|
|
|
|
|
d->setBreakpoint(EVENT, requested.functionName, requested.enabled);
|
|
|
|
|
bp->setPending(false);
|
|
|
|
|
notifyBreakpointInsertOk(bp);
|
2011-08-17 11:47:42 +02:00
|
|
|
}
|
2015-07-08 13:14:03 +02:00
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
d->breakpointsSync.insert(d->sequence, bp);
|
2011-08-17 11:47:42 +02:00
|
|
|
}
|
|
|
|
|
|
2016-04-14 10:38:35 +02:00
|
|
|
void QmlEngine::resetLocation()
|
|
|
|
|
{
|
|
|
|
|
DebuggerEngine::resetLocation();
|
|
|
|
|
d->currentlyLookingUp.clear();
|
|
|
|
|
}
|
|
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
void QmlEngine::removeBreakpoint(const Breakpoint &bp)
|
2011-08-17 11:47:42 +02:00
|
|
|
{
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
QTC_ASSERT(bp, return);
|
|
|
|
|
const BreakpointParameters ¶ms = bp->requestedParameters();
|
2012-02-28 17:30:15 +01:00
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
const BreakpointState state = bp->state();
|
2015-01-10 01:07:01 +01:00
|
|
|
QTC_ASSERT(state == BreakpointRemoveRequested, qDebug() << bp << this << state);
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
notifyBreakpointRemoveProceeding(bp);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
if (params.type == BreakpointAtJavaScriptThrow)
|
|
|
|
|
d->setExceptionBreak(AllExceptions);
|
|
|
|
|
else if (params.type == BreakpointOnQmlSignalEmit)
|
2016-06-07 17:04:53 +02:00
|
|
|
d->setBreakpoint(EVENT, params.functionName, false);
|
2015-07-08 13:14:03 +02:00
|
|
|
else
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
d->clearBreakpoint(bp);
|
2011-08-17 11:47:42 +02:00
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
if (bp->state() == BreakpointRemoveProceeding)
|
|
|
|
|
notifyBreakpointRemoveOk(bp);
|
2011-08-17 11:47:42 +02:00
|
|
|
}
|
|
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
void QmlEngine::updateBreakpoint(const Breakpoint &bp)
|
2011-08-17 11:47:42 +02:00
|
|
|
{
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
QTC_ASSERT(bp, return);
|
|
|
|
|
const BreakpointState state = bp->state();
|
|
|
|
|
QTC_ASSERT(state == BreakpointUpdateRequested, qDebug() << bp << this << state);
|
|
|
|
|
notifyBreakpointChangeProceeding(bp);
|
2011-08-17 11:47:42 +02:00
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
const BreakpointParameters &requested = bp->requestedParameters();
|
2015-07-08 13:14:03 +02:00
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
if (requested.type == BreakpointAtJavaScriptThrow) {
|
|
|
|
|
d->setExceptionBreak(AllExceptions, requested.enabled);
|
|
|
|
|
bp->setEnabled(requested.enabled);
|
|
|
|
|
} else if (requested.type == BreakpointOnQmlSignalEmit) {
|
|
|
|
|
d->setBreakpoint(EVENT, requested.functionName, requested.enabled);
|
|
|
|
|
bp->setEnabled(requested.enabled);
|
2018-07-18 15:41:09 +02:00
|
|
|
} else if (d->canChangeBreakpoint()) {
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
d->changeBreakpoint(bp, requested.enabled);
|
2011-08-17 11:47:42 +02:00
|
|
|
} else {
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
d->clearBreakpoint(bp);
|
2020-01-02 11:31:14 +01:00
|
|
|
d->setBreakpoint(SCRIPTREGEXP, requested.fileName.toString(),
|
2023-06-06 15:44:59 +02:00
|
|
|
requested.enabled, requested.textPosition.line, 0,
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
requested.condition, requested.ignoreCount);
|
|
|
|
|
d->breakpointsSync.insert(d->sequence, bp);
|
2010-06-25 14:08:53 +02:00
|
|
|
}
|
|
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
if (bp->state() == BreakpointUpdateProceeding)
|
|
|
|
|
notifyBreakpointChangeOk(bp);
|
2010-06-09 16:13:47 +02:00
|
|
|
}
|
|
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
bool QmlEngine::acceptsBreakpoint(const BreakpointParameters &bp) const
|
2010-10-05 11:01:14 +02:00
|
|
|
{
|
2011-09-13 12:47:46 +02:00
|
|
|
//TODO: enable setting of breakpoints before start of debug session
|
|
|
|
|
//For now, the event breakpoint can be set after the activeDebuggerClient is known
|
2011-09-28 17:55:48 +02:00
|
|
|
//This is because the older client does not support BreakpointOnQmlSignalHandler
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
if (bp.type == BreakpointOnQmlSignalEmit || bp.type == BreakpointAtJavaScriptThrow)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return bp.isQmlFileAndLineBreakpoint();
|
2010-10-05 11:01:14 +02:00
|
|
|
}
|
|
|
|
|
|
2023-03-14 08:27:50 +01:00
|
|
|
void QmlEngine::loadSymbols(const FilePath &moduleName)
|
2010-06-09 16:13:47 +02:00
|
|
|
{
|
|
|
|
|
Q_UNUSED(moduleName)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlEngine::loadAllSymbols()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlEngine::reloadModules()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-30 15:23:02 +01:00
|
|
|
void QmlEngine::reloadSourceFiles()
|
|
|
|
|
{
|
2015-07-08 13:14:03 +02:00
|
|
|
d->scripts(4, QList<int>(), true, QVariant());
|
2011-11-30 15:23:02 +01:00
|
|
|
}
|
|
|
|
|
|
2015-07-15 17:49:04 +02:00
|
|
|
void QmlEngine::updateAll()
|
|
|
|
|
{
|
|
|
|
|
d->updateLocals();
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-14 08:27:50 +01:00
|
|
|
void QmlEngine::requestModuleSymbols(const FilePath &moduleName)
|
2010-06-09 16:13:47 +02:00
|
|
|
{
|
|
|
|
|
Q_UNUSED(moduleName)
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-08 18:07:11 +02:00
|
|
|
bool QmlEngine::canHandleToolTip(const DebuggerToolTipContext &) const
|
2010-06-09 16:13:47 +02:00
|
|
|
{
|
2011-10-06 17:38:28 +02:00
|
|
|
// This is processed by QML inspector, which has dependencies to
|
2010-10-27 15:23:30 +02:00
|
|
|
// the qml js editor. Makes life easier.
|
2015-03-06 13:36:42 +01:00
|
|
|
// FIXME: Except that there isn't any attached.
|
2011-02-17 10:08:57 +01:00
|
|
|
return true;
|
2010-06-09 16:13:47 +02:00
|
|
|
}
|
|
|
|
|
|
2015-03-19 12:42:53 +01:00
|
|
|
void QmlEngine::assignValueInDebugger(WatchItem *item,
|
2017-10-16 13:22:52 +02:00
|
|
|
const QString &expression, const QVariant &editValue)
|
2010-06-09 16:13:47 +02:00
|
|
|
{
|
2012-05-23 14:55:48 +02:00
|
|
|
if (!expression.isEmpty()) {
|
2023-06-06 16:34:44 +02:00
|
|
|
QTC_CHECK(editValue.typeId() == QVariant::String);
|
2022-02-08 07:52:47 +01:00
|
|
|
QVariant value;
|
|
|
|
|
QString val = editValue.toString();
|
|
|
|
|
if (item->type == "boolean")
|
|
|
|
|
value = val != "false" && val != "0";
|
|
|
|
|
else if (item->type == "number")
|
|
|
|
|
value = val.toDouble();
|
|
|
|
|
else
|
|
|
|
|
value = QString('"' + val.replace('"', "\\\"") + '"');
|
2017-10-16 13:22:52 +02:00
|
|
|
|
2015-08-26 11:51:56 +02:00
|
|
|
if (item->isInspect()) {
|
|
|
|
|
d->inspectorAgent.assignValue(item, expression, value);
|
2015-07-08 13:14:03 +02:00
|
|
|
} else {
|
|
|
|
|
StackHandler *handler = stackHandler();
|
2016-06-07 17:04:53 +02:00
|
|
|
QString exp = QString("%1 = %2;").arg(expression).arg(value.toString());
|
2015-07-08 13:14:03 +02:00
|
|
|
if (handler->isContentsValid() && handler->currentFrame().isUsable()) {
|
2016-12-07 16:43:12 +01:00
|
|
|
d->evaluate(exp, -1, [this](const QVariantMap &) { d->updateLocals(); });
|
2015-07-08 13:14:03 +02:00
|
|
|
} else {
|
2022-07-05 15:37:08 +02:00
|
|
|
showMessage(Tr::tr("Cannot evaluate %1 in current stack frame.")
|
2016-06-07 17:04:53 +02:00
|
|
|
.arg(expression), ConsoleOutput);
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
}
|
2010-07-22 19:06:26 +02:00
|
|
|
}
|
2010-06-09 16:13:47 +02:00
|
|
|
}
|
|
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
void QmlEngine::expandItem(const QString &iname)
|
2010-06-09 16:13:47 +02:00
|
|
|
{
|
2015-06-08 18:07:11 +02:00
|
|
|
const WatchItem *item = watchHandler()->findItem(iname);
|
2015-07-15 17:14:29 +02:00
|
|
|
QTC_ASSERT(item, return);
|
2015-07-07 07:39:04 +02:00
|
|
|
|
2015-03-19 12:42:53 +01:00
|
|
|
if (item->isInspect()) {
|
2015-08-26 11:51:56 +02:00
|
|
|
d->inspectorAgent.updateWatchData(*item);
|
2012-04-18 14:20:54 +02:00
|
|
|
} else {
|
2015-07-15 17:14:29 +02:00
|
|
|
LookupItems items;
|
2015-09-23 13:14:30 +02:00
|
|
|
items.insert(int(item->id), {item->iname, item->name, item->exp});
|
2015-07-15 17:14:29 +02:00
|
|
|
d->lookup(items);
|
2011-02-23 16:09:56 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
void QmlEngine::updateItem(const QString &iname)
|
2015-07-15 17:14:29 +02:00
|
|
|
{
|
|
|
|
|
const WatchItem *item = watchHandler()->findItem(iname);
|
|
|
|
|
QTC_ASSERT(item, return);
|
|
|
|
|
|
2015-07-16 16:14:43 +02:00
|
|
|
if (state() == InferiorStopOk) {
|
|
|
|
|
// The Qt side Q_ASSERTs otherwise. So postpone the evaluation,
|
|
|
|
|
// it will be triggered from from upateLocals() later.
|
2016-06-07 17:04:53 +02:00
|
|
|
QString exp = item->exp;
|
2016-12-07 16:43:12 +01:00
|
|
|
d->evaluate(exp, -1, [this, iname, exp](const QVariantMap &response) {
|
2015-07-16 16:14:43 +02:00
|
|
|
d->handleEvaluateExpression(response, iname, exp);
|
|
|
|
|
});
|
|
|
|
|
}
|
2015-07-15 17:14:29 +02:00
|
|
|
}
|
|
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
void QmlEngine::selectWatchData(const QString &iname)
|
2012-11-23 13:35:44 +01:00
|
|
|
{
|
2015-03-19 12:42:53 +01:00
|
|
|
const WatchItem *item = watchHandler()->findItem(iname);
|
|
|
|
|
if (item && item->isInspect())
|
2015-08-26 11:51:56 +02:00
|
|
|
d->inspectorAgent.watchDataSelected(item->id);
|
2012-11-23 13:35:44 +01:00
|
|
|
}
|
|
|
|
|
|
2015-08-21 17:33:53 +02:00
|
|
|
bool compareConsoleItems(const ConsoleItem *a, const ConsoleItem *b)
|
|
|
|
|
{
|
2018-07-23 22:28:49 +02:00
|
|
|
if (a == nullptr)
|
2015-08-21 17:33:53 +02:00
|
|
|
return true;
|
2018-07-23 22:28:49 +02:00
|
|
|
if (b == nullptr)
|
2015-08-21 17:33:53 +02:00
|
|
|
return false;
|
|
|
|
|
return a->text() < b->text();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ConsoleItem *constructLogItemTree(const QVariant &result,
|
2015-07-08 13:14:03 +02:00
|
|
|
const QString &key = QString())
|
2012-10-04 14:54:59 +02:00
|
|
|
{
|
2021-03-01 08:59:44 +01:00
|
|
|
bool sorted = debuggerSettings()->sortStructMembers.value();
|
2012-10-04 14:54:59 +02:00
|
|
|
if (!result.isValid())
|
2018-07-23 22:28:49 +02:00
|
|
|
return nullptr;
|
2012-10-04 14:54:59 +02:00
|
|
|
|
2015-08-21 17:33:53 +02:00
|
|
|
QString text;
|
2018-02-01 10:59:24 +01:00
|
|
|
ConsoleItem *item = nullptr;
|
2023-06-06 16:34:44 +02:00
|
|
|
if (result.typeId() == QVariant::Map) {
|
2012-10-04 14:54:59 +02:00
|
|
|
if (key.isEmpty())
|
2016-06-07 17:04:53 +02:00
|
|
|
text = "Object";
|
2012-10-04 14:54:59 +02:00
|
|
|
else
|
2016-06-07 17:04:53 +02:00
|
|
|
text = key + " : Object";
|
2012-10-04 14:54:59 +02:00
|
|
|
|
2019-07-24 13:43:54 +02:00
|
|
|
const QMap<QString, QVariant> resultMap = result.toMap();
|
2015-08-21 17:33:53 +02:00
|
|
|
QVarLengthArray<ConsoleItem *> children(resultMap.size());
|
|
|
|
|
auto it = children.begin();
|
2019-07-24 13:43:54 +02:00
|
|
|
for (auto i = resultMap.cbegin(), end = resultMap.cend(); i != end; ++i) {
|
2015-08-21 17:33:53 +02:00
|
|
|
*(it++) = constructLogItemTree(i.value(), i.key());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sort before inserting as ConsoleItem::sortChildren causes a whole cascade of changes we
|
|
|
|
|
// may not want to handle here.
|
|
|
|
|
if (sorted)
|
|
|
|
|
std::sort(children.begin(), children.end(), compareConsoleItems);
|
|
|
|
|
|
|
|
|
|
item = new ConsoleItem(ConsoleItem::DefaultType, text);
|
2022-10-07 14:46:06 +02:00
|
|
|
for (ConsoleItem *child : std::as_const(children)) {
|
2012-10-04 14:54:59 +02:00
|
|
|
if (child)
|
2015-08-21 17:33:53 +02:00
|
|
|
item->appendChild(child);
|
2012-10-04 14:54:59 +02:00
|
|
|
}
|
2015-08-21 17:33:53 +02:00
|
|
|
|
2023-06-09 13:15:11 +02:00
|
|
|
} else if (result.typeId() == QVariant::List) {
|
2012-10-04 14:54:59 +02:00
|
|
|
if (key.isEmpty())
|
2016-06-07 17:04:53 +02:00
|
|
|
text = "List";
|
2012-10-04 14:54:59 +02:00
|
|
|
else
|
2016-06-07 17:04:53 +02:00
|
|
|
text = QString("[%1] : List").arg(key);
|
2015-08-21 17:33:53 +02:00
|
|
|
|
2012-10-04 14:54:59 +02:00
|
|
|
QVariantList resultList = result.toList();
|
2015-08-21 17:33:53 +02:00
|
|
|
QVarLengthArray<ConsoleItem *> children(resultList.size());
|
|
|
|
|
for (int i = 0; i < resultList.count(); i++)
|
|
|
|
|
children[i] = constructLogItemTree(resultList.at(i), QString::number(i));
|
|
|
|
|
|
|
|
|
|
if (sorted)
|
|
|
|
|
std::sort(children.begin(), children.end(), compareConsoleItems);
|
|
|
|
|
|
|
|
|
|
item = new ConsoleItem(ConsoleItem::DefaultType, text);
|
2022-10-07 14:46:06 +02:00
|
|
|
for (ConsoleItem *child : std::as_const(children)) {
|
2012-10-04 14:54:59 +02:00
|
|
|
if (child)
|
2015-08-21 17:33:53 +02:00
|
|
|
item->appendChild(child);
|
2012-10-04 14:54:59 +02:00
|
|
|
}
|
|
|
|
|
} else if (result.canConvert(QVariant::String)) {
|
2015-08-21 17:33:53 +02:00
|
|
|
item = new ConsoleItem(ConsoleItem::DefaultType, result.toString());
|
2012-10-04 14:54:59 +02:00
|
|
|
} else {
|
2016-06-07 17:04:53 +02:00
|
|
|
item = new ConsoleItem(ConsoleItem::DefaultType, "Unknown Value");
|
2012-10-04 14:54:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return item;
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-07 16:19:55 +01:00
|
|
|
void QmlEngine::expressionEvaluated(quint32 queryId, const QVariant &result)
|
2012-02-15 12:35:43 +01:00
|
|
|
{
|
2015-07-08 13:14:03 +02:00
|
|
|
if (d->queryIds.contains(queryId)) {
|
|
|
|
|
d->queryIds.removeOne(queryId);
|
2015-11-10 16:59:02 +01:00
|
|
|
if (ConsoleItem *item = constructLogItemTree(result))
|
|
|
|
|
debuggerConsole()->printItem(item);
|
2012-03-07 16:19:55 +01:00
|
|
|
}
|
2012-02-15 12:35:43 +01:00
|
|
|
}
|
|
|
|
|
|
2012-01-12 20:28:17 +01:00
|
|
|
bool QmlEngine::hasCapability(unsigned cap) const
|
2010-07-16 11:44:29 +02:00
|
|
|
{
|
2012-01-12 20:28:17 +01:00
|
|
|
return cap & (AddWatcherCapability
|
2011-12-22 18:43:09 +01:00
|
|
|
| AddWatcherWhileRunningCapability
|
2018-09-24 14:41:51 +02:00
|
|
|
| RunToLineCapability
|
|
|
|
|
| WatchComplexExpressionsCapability);
|
2010-07-16 11:44:29 +02:00
|
|
|
/*ReverseSteppingCapability | SnapshotCapability
|
|
|
|
|
| AutoDerefPointersCapability | DisassemblerCapability
|
|
|
|
|
| RegisterCapability | ShowMemoryCapability
|
|
|
|
|
| JumpToLineCapability | ReloadModuleCapability
|
|
|
|
|
| ReloadModuleSymbolsCapability | BreakOnThrowAndCatchCapability
|
|
|
|
|
| ReturnFromFunctionCapability
|
|
|
|
|
| CreateFullBacktraceCapability
|
|
|
|
|
| WatchpointCapability
|
|
|
|
|
| AddWatcherCapability;*/
|
2010-06-25 14:08:53 +02:00
|
|
|
}
|
|
|
|
|
|
2012-06-12 10:48:54 +02:00
|
|
|
void QmlEngine::quitDebugger()
|
|
|
|
|
{
|
2015-07-08 13:14:03 +02:00
|
|
|
d->automaticConnect = false;
|
|
|
|
|
d->retryOnConnectFail = false;
|
2022-05-04 16:30:55 +02:00
|
|
|
stopProcess();
|
2017-11-07 17:55:05 +01:00
|
|
|
closeConnection();
|
2012-06-12 10:48:54 +02:00
|
|
|
}
|
|
|
|
|
|
2016-11-03 15:52:30 +01:00
|
|
|
void QmlEngine::doUpdateLocals(const UpdateParameters ¶ms)
|
|
|
|
|
{
|
2019-07-23 10:58:00 +02:00
|
|
|
Q_UNUSED(params)
|
2016-11-03 15:52:30 +01:00
|
|
|
d->updateLocals();
|
|
|
|
|
}
|
|
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
Context QmlEngine::languageContext() const
|
|
|
|
|
{
|
|
|
|
|
return Context(Constants::C_QMLDEBUGGER);
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-10 14:42:44 +02:00
|
|
|
void QmlEngine::disconnected()
|
|
|
|
|
{
|
2022-07-05 15:37:08 +02:00
|
|
|
showMessage(Tr::tr("QML Debugger disconnected."), StatusBar);
|
2010-08-10 14:42:44 +02:00
|
|
|
notifyInferiorExited();
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-15 12:35:43 +01:00
|
|
|
void QmlEngine::updateCurrentContext()
|
|
|
|
|
{
|
2017-05-19 13:14:44 +02:00
|
|
|
d->inspectorAgent.enableTools(state() == InferiorRunOk);
|
|
|
|
|
|
2012-10-05 13:42:14 +02:00
|
|
|
QString context;
|
2016-12-09 12:51:50 +01:00
|
|
|
switch (state()) {
|
|
|
|
|
case InferiorStopOk:
|
2012-10-05 13:42:14 +02:00
|
|
|
context = stackHandler()->currentFrame().function;
|
2016-12-09 12:51:50 +01:00
|
|
|
break;
|
|
|
|
|
case InferiorRunOk:
|
|
|
|
|
if (d->contextEvaluate || !d->unpausedEvaluate) {
|
|
|
|
|
// !unpausedEvaluate means we are using the old QQmlEngineDebugService which understands
|
|
|
|
|
// context. contextEvaluate means the V4 debug service can handle context.
|
|
|
|
|
QModelIndex currentIndex = inspectorView()->currentIndex();
|
|
|
|
|
const WatchItem *currentData = watchHandler()->watchItem(currentIndex);
|
|
|
|
|
if (!currentData)
|
|
|
|
|
return;
|
|
|
|
|
const WatchItem *parentData = watchHandler()->watchItem(currentIndex.parent());
|
|
|
|
|
const WatchItem *grandParentData = watchHandler()->watchItem(currentIndex.parent().parent());
|
|
|
|
|
if (currentData->id != parentData->id)
|
|
|
|
|
context = currentData->name;
|
|
|
|
|
else if (parentData->id != grandParentData->id)
|
|
|
|
|
context = parentData->name;
|
|
|
|
|
else
|
|
|
|
|
context = grandParentData->name;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
// No context. Clear the label
|
|
|
|
|
debuggerConsole()->setContext(QString());
|
|
|
|
|
return;
|
2012-10-05 13:42:14 +02:00
|
|
|
}
|
2016-12-09 11:32:48 +01:00
|
|
|
|
2022-07-05 15:37:08 +02:00
|
|
|
debuggerConsole()->setContext(Tr::tr("Context:") + ' '
|
|
|
|
|
+ (context.isEmpty() ? Tr::tr("Global QML Context") : context));
|
2012-02-15 12:35:43 +01:00
|
|
|
}
|
|
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
void QmlEngine::executeDebuggerCommand(const QString &command)
|
2012-02-15 12:35:43 +01:00
|
|
|
{
|
2015-11-30 14:15:54 +01:00
|
|
|
if (state() == InferiorStopOk) {
|
|
|
|
|
StackHandler *handler = stackHandler();
|
|
|
|
|
if (handler->isContentsValid() && handler->currentFrame().isUsable()) {
|
2016-12-07 16:43:12 +01:00
|
|
|
d->evaluate(command, -1, CB(d->handleExecuteDebuggerCommand));
|
2012-02-15 12:35:43 +01:00
|
|
|
} else {
|
2015-11-30 14:15:54 +01:00
|
|
|
// Paused but no stack? Something is wrong
|
2016-06-07 17:04:53 +02:00
|
|
|
d->engine->showMessage(QString("Cannot evaluate %1. The stack trace is broken.").arg(command),
|
2015-11-30 14:15:54 +01:00
|
|
|
ConsoleOutput);
|
2012-02-15 12:35:43 +01:00
|
|
|
}
|
2012-10-04 14:54:59 +02:00
|
|
|
} else {
|
2016-05-23 11:35:44 +02:00
|
|
|
QModelIndex currentIndex = inspectorView()->currentIndex();
|
2016-12-07 16:43:12 +01:00
|
|
|
qint64 contextId = watchHandler()->watchItem(currentIndex)->id;
|
|
|
|
|
|
|
|
|
|
if (d->unpausedEvaluate) {
|
|
|
|
|
d->evaluate(command, contextId, CB(d->handleExecuteDebuggerCommand));
|
2016-05-23 11:35:44 +02:00
|
|
|
} else {
|
2018-11-15 17:14:48 +01:00
|
|
|
quint32 queryId = d->inspectorAgent.queryExpressionResult(
|
|
|
|
|
contextId, command,
|
|
|
|
|
d->inspectorAgent.engineId(watchHandler()->watchItem(currentIndex)));
|
2016-12-07 16:43:12 +01:00
|
|
|
if (queryId) {
|
|
|
|
|
d->queryIds.append(queryId);
|
|
|
|
|
} else {
|
|
|
|
|
d->engine->showMessage("The application has to be stopped in a breakpoint in order to"
|
|
|
|
|
" evaluate expressions", ConsoleOutput);
|
|
|
|
|
}
|
2016-05-23 11:35:44 +02:00
|
|
|
}
|
2012-02-15 12:35:43 +01:00
|
|
|
}
|
|
|
|
|
}
|
2010-09-08 13:31:12 +02:00
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
bool QmlEngine::companionPreventsActions() const
|
|
|
|
|
{
|
|
|
|
|
// We need a C++ Engine in a Running state to do anything sensible
|
|
|
|
|
// as otherwise the debugger services in the debuggee are unresponsive.
|
|
|
|
|
if (DebuggerEngine *companion = companionEngine())
|
|
|
|
|
return companion->state() != InferiorRunOk;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
void QmlEnginePrivate::updateScriptSource(const QString &fileName, int lineOffset, int columnOffset,
|
|
|
|
|
const QString &source)
|
2011-11-30 15:23:02 +01:00
|
|
|
{
|
2018-02-01 10:59:24 +01:00
|
|
|
QTextDocument *document = nullptr;
|
2015-07-08 13:14:03 +02:00
|
|
|
if (sourceDocuments.contains(fileName)) {
|
|
|
|
|
document = sourceDocuments.value(fileName);
|
2011-11-30 15:23:02 +01:00
|
|
|
} else {
|
|
|
|
|
document = new QTextDocument(this);
|
2015-07-08 13:14:03 +02:00
|
|
|
sourceDocuments.insert(fileName, document);
|
2011-11-30 15:23:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We're getting an unordered set of snippets that can even interleave
|
|
|
|
|
// Therefore we've to carefully update the existing document
|
|
|
|
|
|
|
|
|
|
QTextCursor cursor(document);
|
|
|
|
|
for (int i = 0; i < lineOffset; ++i) {
|
|
|
|
|
if (!cursor.movePosition(QTextCursor::NextBlock))
|
|
|
|
|
cursor.insertBlock();
|
|
|
|
|
}
|
|
|
|
|
QTC_CHECK(cursor.blockNumber() == lineOffset);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < columnOffset; ++i) {
|
|
|
|
|
if (!cursor.movePosition(QTextCursor::NextCharacter))
|
2016-06-07 17:04:53 +02:00
|
|
|
cursor.insertText(" ");
|
2011-11-30 15:23:02 +01:00
|
|
|
}
|
|
|
|
|
QTC_CHECK(cursor.positionInBlock() == columnOffset);
|
|
|
|
|
|
2018-05-30 15:42:51 +02:00
|
|
|
const QStringList lines = source.split('\n');
|
|
|
|
|
for (QString line : lines) {
|
2016-06-07 17:04:53 +02:00
|
|
|
if (line.endsWith('\r'))
|
2011-11-30 15:23:02 +01:00
|
|
|
line.remove(line.size() -1, 1);
|
|
|
|
|
|
|
|
|
|
// line already there?
|
|
|
|
|
QTextCursor existingCursor(cursor);
|
|
|
|
|
existingCursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
|
|
|
|
if (existingCursor.selectedText() != line)
|
|
|
|
|
cursor.insertText(line);
|
|
|
|
|
|
|
|
|
|
if (!cursor.movePosition(QTextCursor::NextBlock))
|
|
|
|
|
cursor.insertBlock();
|
|
|
|
|
}
|
2011-12-21 10:39:54 +01:00
|
|
|
|
|
|
|
|
//update open editors
|
2023-02-07 22:46:35 +01:00
|
|
|
QString titlePattern = Tr::tr("JS Source for %1").arg(fileName);
|
2011-12-21 10:39:54 +01:00
|
|
|
//Check if there are open editors with the same title
|
2022-04-29 12:43:37 +02:00
|
|
|
const QList<IDocument *> documents = DocumentModel::openedDocuments();
|
|
|
|
|
for (IDocument *doc: documents) {
|
2013-07-09 12:14:33 +02:00
|
|
|
if (doc->displayName() == titlePattern) {
|
|
|
|
|
updateDocument(doc, document);
|
2011-12-21 10:39:54 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
bool QmlEnginePrivate::canEvaluateScript(const QString &script)
|
2011-12-21 10:39:54 +01:00
|
|
|
{
|
2015-07-08 13:14:03 +02:00
|
|
|
interpreter.clearText();
|
|
|
|
|
interpreter.appendText(script);
|
|
|
|
|
return interpreter.canEvaluate();
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-15 13:09:38 +02:00
|
|
|
void QmlEngine::connectionFailed()
|
2015-07-08 13:14:03 +02:00
|
|
|
{
|
|
|
|
|
// this is only an error if we are already connected and something goes wrong.
|
|
|
|
|
if (isConnected()) {
|
2022-07-05 15:37:08 +02:00
|
|
|
showMessage(Tr::tr("QML Debugger: Connection failed."), StatusBar);
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
notifyInferiorSpontaneousStop();
|
|
|
|
|
notifyInferiorIll();
|
2015-07-08 13:14:03 +02:00
|
|
|
} else {
|
|
|
|
|
d->connectionTimer.stop();
|
|
|
|
|
connectionStartupFailed();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlEngine::checkConnectionState()
|
|
|
|
|
{
|
|
|
|
|
if (!isConnected()) {
|
|
|
|
|
closeConnection();
|
|
|
|
|
connectionStartupFailed();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool QmlEngine::isConnected() const
|
|
|
|
|
{
|
2017-09-07 15:17:15 +02:00
|
|
|
if (QmlDebugConnection *connection = d->connection())
|
|
|
|
|
return connection->isConnected();
|
|
|
|
|
else
|
|
|
|
|
return false;
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlEngine::showConnectionStateMessage(const QString &message)
|
|
|
|
|
{
|
2016-06-07 17:04:53 +02:00
|
|
|
showMessage("QML Debugger: " + message, LogStatus);
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlEngine::logServiceStateChange(const QString &service, float version,
|
2016-06-07 17:04:53 +02:00
|
|
|
QmlDebugClient::State newState)
|
2015-07-08 13:14:03 +02:00
|
|
|
{
|
|
|
|
|
switch (newState) {
|
|
|
|
|
case QmlDebugClient::Unavailable: {
|
2016-06-07 17:04:53 +02:00
|
|
|
showConnectionStateMessage(QString("Status of \"%1\" Version: %2 changed to 'unavailable'.").
|
|
|
|
|
arg(service).arg(version));
|
2015-07-08 13:14:03 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case QmlDebugClient::Enabled: {
|
2016-06-07 17:04:53 +02:00
|
|
|
showConnectionStateMessage(QString("Status of \"%1\" Version: %2 changed to 'enabled'.").
|
|
|
|
|
arg(service).arg(version));
|
2015-07-08 13:14:03 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case QmlDebugClient::NotConnected: {
|
2016-06-07 17:04:53 +02:00
|
|
|
showConnectionStateMessage(QString("Status of \"%1\" Version: %2 changed to 'not connected'.").
|
|
|
|
|
arg(service).arg(version));
|
2015-07-08 13:14:03 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlEngine::logServiceActivity(const QString &service, const QString &logMessage)
|
|
|
|
|
{
|
2016-06-07 17:04:53 +02:00
|
|
|
showMessage(service + ' ' + logMessage, LogDebug);
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlEnginePrivate::continueDebugging(StepAction action)
|
|
|
|
|
{
|
|
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "request",
|
|
|
|
|
// "command" : "continue",
|
|
|
|
|
// "arguments" : { "stepaction" : <"in", "next" or "out">,
|
|
|
|
|
// "stepcount" : <number of steps (default 1)>
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
2015-07-10 11:06:27 +02:00
|
|
|
DebuggerCommand cmd(CONTINEDEBUGGING);
|
|
|
|
|
|
|
|
|
|
if (action == StepIn)
|
|
|
|
|
cmd.arg(STEPACTION, IN);
|
|
|
|
|
else if (action == StepOut)
|
|
|
|
|
cmd.arg(STEPACTION, OUT);
|
|
|
|
|
else if (action == Next)
|
|
|
|
|
cmd.arg(STEPACTION, NEXT);
|
|
|
|
|
|
|
|
|
|
runCommand(cmd);
|
|
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
previousStepAction = action;
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-07 16:43:12 +01:00
|
|
|
void QmlEnginePrivate::evaluate(const QString expr, qint64 context, const QmlCallback &cb)
|
2015-07-08 13:14:03 +02:00
|
|
|
{
|
|
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "request",
|
|
|
|
|
// "command" : "evaluate",
|
|
|
|
|
// "arguments" : { "expression" : <expression to evaluate>,
|
|
|
|
|
// "frame" : <number>,
|
|
|
|
|
// "global" : <boolean>,
|
|
|
|
|
// "disable_break" : <boolean>,
|
2016-12-07 16:43:12 +01:00
|
|
|
// "context" : <object id>
|
2015-07-08 13:14:03 +02:00
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
2015-07-16 16:14:43 +02:00
|
|
|
// 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)
|
2015-11-30 14:15:54 +01:00
|
|
|
QTC_ASSERT(unpausedEvaluate || engine->state() == InferiorStopOk, return);
|
2015-07-16 16:14:43 +02:00
|
|
|
|
2015-07-10 11:06:27 +02:00
|
|
|
DebuggerCommand cmd(EVALUATE);
|
|
|
|
|
|
|
|
|
|
cmd.arg(EXPRESSION, expr);
|
2015-11-30 14:15:54 +01:00
|
|
|
StackHandler *handler = engine->stackHandler();
|
|
|
|
|
if (handler->currentFrame().isUsable())
|
|
|
|
|
cmd.arg(FRAME, handler->currentIndex());
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2016-12-07 16:43:12 +01:00
|
|
|
if (context >= 0)
|
|
|
|
|
cmd.arg(CONTEXT, context);
|
|
|
|
|
|
2015-07-14 16:59:46 +02:00
|
|
|
runCommand(cmd, cb);
|
|
|
|
|
}
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2015-07-16 14:12:16 +02:00
|
|
|
void QmlEnginePrivate::handleEvaluateExpression(const QVariantMap &response,
|
2016-06-07 17:04:53 +02:00
|
|
|
const QString &iname,
|
2015-07-16 14:12:16 +02:00
|
|
|
const QString &exp)
|
2015-07-14 16:59:46 +02:00
|
|
|
{
|
|
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "response",
|
|
|
|
|
// "request_seq" : <number>,
|
|
|
|
|
// "command" : "evaluate",
|
|
|
|
|
// "body" : ...
|
|
|
|
|
// "running" : <is the VM running after sending this response>
|
|
|
|
|
// "success" : true
|
|
|
|
|
// }
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
QVariant bodyVal = response.value(BODY).toMap();
|
2015-07-14 16:59:46 +02:00
|
|
|
QmlV8ObjectData body = extractData(bodyVal);
|
|
|
|
|
WatchHandler *watchHandler = engine->watchHandler();
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2015-12-16 17:17:38 +01:00
|
|
|
auto item = new WatchItem;
|
|
|
|
|
item->iname = iname;
|
|
|
|
|
item->name = exp;
|
2016-06-07 17:04:53 +02:00
|
|
|
item->exp = exp;
|
2015-07-14 16:59:46 +02:00
|
|
|
item->id = body.handle;
|
2016-06-07 17:04:53 +02:00
|
|
|
bool success = response.value("success").toBool();
|
2015-07-14 16:59:46 +02:00
|
|
|
if (success) {
|
|
|
|
|
item->type = body.type;
|
|
|
|
|
item->value = body.value.toString();
|
2018-07-09 14:58:44 +02:00
|
|
|
setWatchItemHasChildren(item, body.hasChildren());
|
2015-07-14 16:59:46 +02:00
|
|
|
} else {
|
|
|
|
|
//Do not set type since it is unknown
|
|
|
|
|
item->setError(body.value.toString());
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
2015-07-14 16:59:46 +02:00
|
|
|
insertSubItems(item, body.properties);
|
2015-07-16 14:12:16 +02:00
|
|
|
watchHandler->insertItem(item);
|
2017-10-26 16:32:48 +02:00
|
|
|
watchHandler->updateLocalsWindow();
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-14 16:59:46 +02:00
|
|
|
void QmlEnginePrivate::lookup(const LookupItems &items)
|
2015-07-08 13:14:03 +02:00
|
|
|
{
|
|
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "request",
|
|
|
|
|
// "command" : "lookup",
|
|
|
|
|
// "arguments" : { "handles" : <array of handles>,
|
|
|
|
|
// "includeSource" : <boolean indicating whether
|
|
|
|
|
// the source will be included when
|
|
|
|
|
// script objects are returned>,
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
2015-07-14 16:59:46 +02:00
|
|
|
if (items.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
2016-04-13 15:13:54 +02:00
|
|
|
QList<int> handles;
|
|
|
|
|
for (auto it = items.begin(); it != items.end(); ++it) {
|
|
|
|
|
const int handle = it.key();
|
|
|
|
|
if (!currentlyLookingUp.contains(handle)) {
|
|
|
|
|
currentlyLookingUp.insert(handle, it.value());
|
|
|
|
|
handles.append(handle);
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-07-14 16:59:46 +02:00
|
|
|
|
2015-07-10 11:06:27 +02:00
|
|
|
DebuggerCommand cmd(LOOKUP);
|
|
|
|
|
cmd.arg(HANDLES, handles);
|
2015-07-14 16:24:26 +02:00
|
|
|
runCommand(cmd, CB(handleLookup));
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-14 16:24:26 +02:00
|
|
|
void QmlEnginePrivate::backtrace()
|
2015-07-08 13:14:03 +02:00
|
|
|
{
|
|
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "request",
|
|
|
|
|
// "command" : "backtrace",
|
|
|
|
|
// "arguments" : { "fromFrame" : <number>
|
|
|
|
|
// "toFrame" : <number>
|
|
|
|
|
// "bottom" : <boolean, set to true if the bottom of the
|
|
|
|
|
// stack is requested>
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
2015-07-10 11:06:27 +02:00
|
|
|
DebuggerCommand cmd(BACKTRACE);
|
2015-07-14 16:24:26 +02:00
|
|
|
runCommand(cmd, CB(handleBacktrace));
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-14 16:59:46 +02:00
|
|
|
void QmlEnginePrivate::updateLocals()
|
2015-07-08 13:14:03 +02:00
|
|
|
{
|
|
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "request",
|
|
|
|
|
// "command" : "frame",
|
2015-07-14 16:59:46 +02:00
|
|
|
// "arguments" : { "number" : <frame number> }
|
2015-07-08 13:14:03 +02:00
|
|
|
// }
|
|
|
|
|
|
2015-07-10 11:06:27 +02:00
|
|
|
DebuggerCommand cmd(FRAME);
|
2015-07-14 16:59:46 +02:00
|
|
|
cmd.arg(NUMBER, stackIndexLookup.value(engine->stackHandler()->currentIndex()));
|
2015-07-14 16:24:26 +02:00
|
|
|
runCommand(cmd, CB(handleFrame));
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlEnginePrivate::scope(int number, int frameNumber)
|
|
|
|
|
{
|
|
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "request",
|
|
|
|
|
// "command" : "scope",
|
|
|
|
|
// "arguments" : { "number" : <scope number>
|
|
|
|
|
// "frameNumber" : <frame number, optional uses selected
|
|
|
|
|
// frame if missing>
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
2015-07-10 11:06:27 +02:00
|
|
|
DebuggerCommand cmd(SCOPE);
|
|
|
|
|
cmd.arg(NUMBER, number);
|
|
|
|
|
if (frameNumber != -1)
|
|
|
|
|
cmd.arg(FRAMENUMBER, frameNumber);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2015-07-14 16:24:26 +02:00
|
|
|
runCommand(cmd, CB(handleScope));
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlEnginePrivate::scripts(int types, const QList<int> ids, bool includeSource,
|
|
|
|
|
const QVariant filter)
|
|
|
|
|
{
|
|
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "request",
|
|
|
|
|
// "command" : "scripts",
|
|
|
|
|
// "arguments" : { "types" : <types of scripts to retrieve
|
|
|
|
|
// set bit 0 for native scripts
|
|
|
|
|
// set bit 1 for extension scripts
|
|
|
|
|
// set bit 2 for normal scripts
|
|
|
|
|
// (default is 4 for normal scripts)>
|
|
|
|
|
// "ids" : <array of id's of scripts to return. If this is not specified all scripts are requrned>
|
|
|
|
|
// "includeSource" : <boolean indicating whether the source code should be included for the scripts returned>
|
|
|
|
|
// "filter" : <string or number: filter string or script id.
|
|
|
|
|
// If a number is specified, then only the script with the same number as its script id will be retrieved.
|
|
|
|
|
// If a string is specified, then only scripts whose names contain the filter string will be retrieved.>
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
2015-07-10 11:06:27 +02:00
|
|
|
DebuggerCommand cmd(SCRIPTS);
|
|
|
|
|
cmd.arg(TYPES, types);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2020-01-15 19:10:34 +01:00
|
|
|
if (!ids.isEmpty())
|
2015-07-10 11:06:27 +02:00
|
|
|
cmd.arg(IDS, ids);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
if (includeSource)
|
2015-07-10 11:06:27 +02:00
|
|
|
cmd.arg(INCLUDESOURCE, includeSource);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2023-06-09 13:15:11 +02:00
|
|
|
if (filter.typeId() == QVariant::String)
|
2015-07-10 11:06:27 +02:00
|
|
|
cmd.arg(FILTER, filter.toString());
|
2023-06-09 13:15:11 +02:00
|
|
|
else if (filter.typeId() == QVariant::Int)
|
2015-07-10 11:06:27 +02:00
|
|
|
cmd.arg(FILTER, filter.toInt());
|
2015-07-08 13:14:03 +02:00
|
|
|
else
|
|
|
|
|
QTC_CHECK(!filter.isValid());
|
|
|
|
|
|
2015-07-10 11:06:27 +02:00
|
|
|
runCommand(cmd);
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
|
2022-06-14 11:34:02 +02:00
|
|
|
static QString targetFile(const FilePath &original)
|
|
|
|
|
{
|
|
|
|
|
auto projectTree = ProjectExplorer::ProjectTree::instance();
|
|
|
|
|
auto node = projectTree->nodeForFile(original);
|
|
|
|
|
|
|
|
|
|
if (auto resourceNode = dynamic_cast<ProjectExplorer::ResourceFileNode *>(node))
|
|
|
|
|
return QLatin1String("qrc:") + resourceNode->qrcPath();
|
|
|
|
|
|
|
|
|
|
return original.fileName();
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
void QmlEnginePrivate::setBreakpoint(const QString type, const QString target,
|
|
|
|
|
bool enabled, int line, int column,
|
|
|
|
|
const QString condition, int ignoreCount)
|
|
|
|
|
{
|
|
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "request",
|
|
|
|
|
// "command" : "setbreakpoint",
|
|
|
|
|
// "arguments" : { "type" : <"function" or "script" or "scriptId" or "scriptRegExp">
|
|
|
|
|
// "target" : <function expression or script identification>
|
|
|
|
|
// "line" : <line in script or function>
|
|
|
|
|
// "column" : <character position within the line>
|
|
|
|
|
// "enabled" : <initial enabled state. True or false, default is true>
|
|
|
|
|
// "condition" : <string with break point condition>
|
|
|
|
|
// "ignoreCount" : <number specifying the number of break point hits to ignore, default value is 0>
|
|
|
|
|
// }
|
|
|
|
|
// }
|
2016-06-07 17:04:53 +02:00
|
|
|
if (type == EVENT) {
|
2017-09-07 15:17:15 +02:00
|
|
|
QPacket rs(dataStreamVersion());
|
2015-07-08 13:14:03 +02:00
|
|
|
rs << target.toUtf8() << enabled;
|
2016-06-07 17:04:53 +02:00
|
|
|
engine->showMessage(QString("%1 %2 %3")
|
2020-06-23 18:54:52 +02:00
|
|
|
.arg(QString(BREAKONSIGNAL), target, QLatin1String(enabled ? "enabled" : "disabled")), LogInput);
|
2015-11-16 16:46:51 +01:00
|
|
|
runDirectCommand(BREAKONSIGNAL, rs.data());
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
} else {
|
2015-07-10 11:06:27 +02:00
|
|
|
DebuggerCommand cmd(SETBREAKPOINT);
|
|
|
|
|
cmd.arg(TYPE, type);
|
|
|
|
|
cmd.arg(ENABLED, enabled);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
if (type == SCRIPTREGEXP)
|
2022-06-14 11:34:02 +02:00
|
|
|
cmd.arg(TARGET, targetFile(FilePath::fromString(target)));
|
2015-07-08 13:14:03 +02:00
|
|
|
else
|
2015-07-10 11:06:27 +02:00
|
|
|
cmd.arg(TARGET, target);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
if (line)
|
2015-07-10 11:06:27 +02:00
|
|
|
cmd.arg(LINE, line - 1);
|
2015-07-08 13:14:03 +02:00
|
|
|
if (column)
|
2015-07-10 11:06:27 +02:00
|
|
|
cmd.arg(COLUMN, column - 1);
|
2015-07-08 13:14:03 +02:00
|
|
|
if (!condition.isEmpty())
|
2015-07-10 11:06:27 +02:00
|
|
|
cmd.arg(CONDITION, condition);
|
2015-07-08 13:14:03 +02:00
|
|
|
if (ignoreCount != -1)
|
2015-07-10 11:06:27 +02:00
|
|
|
cmd.arg(IGNORECOUNT, ignoreCount);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2015-07-10 11:06:27 +02:00
|
|
|
runCommand(cmd);
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
void QmlEnginePrivate::clearBreakpoint(const Breakpoint &bp)
|
2015-07-08 13:14:03 +02:00
|
|
|
{
|
|
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "request",
|
|
|
|
|
// "command" : "clearbreakpoint",
|
|
|
|
|
// "arguments" : { "breakpoint" : <number of the break point to clear>
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
2015-07-10 11:06:27 +02:00
|
|
|
DebuggerCommand cmd(CLEARBREAKPOINT);
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
cmd.arg(BREAKPOINT, bp->responseId().toInt());
|
2015-07-10 11:06:27 +02:00
|
|
|
runCommand(cmd);
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
|
2018-07-18 15:41:09 +02:00
|
|
|
bool QmlEnginePrivate::canChangeBreakpoint() const
|
|
|
|
|
{
|
|
|
|
|
return supportChangeBreakpoint;
|
|
|
|
|
}
|
|
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
void QmlEnginePrivate::changeBreakpoint(const Breakpoint &bp, bool enabled)
|
2018-07-18 15:41:09 +02:00
|
|
|
{
|
|
|
|
|
DebuggerCommand cmd(CHANGEBREAKPOINT);
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
cmd.arg(BREAKPOINT, bp->responseId().toInt());
|
2018-07-18 15:41:09 +02:00
|
|
|
cmd.arg(ENABLED, enabled);
|
|
|
|
|
runCommand(cmd);
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
void QmlEnginePrivate::setExceptionBreak(Exceptions type, bool enabled)
|
|
|
|
|
{
|
|
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "request",
|
|
|
|
|
// "command" : "setexceptionbreak",
|
|
|
|
|
// "arguments" : { "type" : <string: "all", or "uncaught">,
|
|
|
|
|
// "enabled" : <optional bool: enables the break type if true>
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
2015-07-10 11:06:27 +02:00
|
|
|
DebuggerCommand cmd(SETEXCEPTIONBREAK);
|
2015-07-08 13:14:03 +02:00
|
|
|
if (type == AllExceptions)
|
2015-07-10 11:06:27 +02:00
|
|
|
cmd.arg(TYPE, ALL);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2015-07-10 11:06:27 +02:00
|
|
|
//Not Supported:
|
|
|
|
|
// else if (type == UncaughtExceptions)
|
|
|
|
|
// cmd.args(TYPE, UNCAUGHT);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2015-07-10 11:06:27 +02:00
|
|
|
if (enabled)
|
|
|
|
|
cmd.arg(ENABLED, enabled);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2015-07-10 11:06:27 +02:00
|
|
|
runCommand(cmd);
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-13 13:36:28 +02:00
|
|
|
QmlV8ObjectData QmlEnginePrivate::extractData(const QVariant &data) const
|
2015-07-08 13:14:03 +02:00
|
|
|
{
|
|
|
|
|
// { "handle" : <handle>,
|
|
|
|
|
// "type" : <"undefined", "null", "boolean", "number", "string", "object", "function" or "frame">
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// {"handle":<handle>,"type":"undefined"}
|
|
|
|
|
|
|
|
|
|
// {"handle":<handle>,"type":"null"}
|
|
|
|
|
|
|
|
|
|
// { "handle":<handle>,
|
|
|
|
|
// "type" : <"boolean", "number" or "string">
|
|
|
|
|
// "value" : <JSON encoded value>
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// {"handle":7,"type":"boolean","value":true}
|
|
|
|
|
|
|
|
|
|
// {"handle":8,"type":"number","value":42}
|
|
|
|
|
|
|
|
|
|
// { "handle" : <handle>,
|
|
|
|
|
// "type" : "object",
|
|
|
|
|
// "className" : <Class name, ECMA-262 property [[Class]]>,
|
|
|
|
|
// "constructorFunction" : {"ref":<handle>},
|
|
|
|
|
// "protoObject" : {"ref":<handle>},
|
|
|
|
|
// "prototypeObject" : {"ref":<handle>},
|
|
|
|
|
// "properties" : [ {"name" : <name>,
|
|
|
|
|
// "ref" : <handle>
|
|
|
|
|
// },
|
|
|
|
|
// ...
|
|
|
|
|
// ]
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// { "handle" : <handle>,
|
|
|
|
|
// "type" : "function",
|
|
|
|
|
// "className" : "Function",
|
|
|
|
|
// "constructorFunction" : {"ref":<handle>},
|
|
|
|
|
// "protoObject" : {"ref":<handle>},
|
|
|
|
|
// "prototypeObject" : {"ref":<handle>},
|
|
|
|
|
// "name" : <function name>,
|
|
|
|
|
// "inferredName" : <inferred function name for anonymous functions>
|
|
|
|
|
// "source" : <function source>,
|
|
|
|
|
// "script" : <reference to function script>,
|
|
|
|
|
// "scriptId" : <id of function script>,
|
|
|
|
|
// "position" : <function begin position in script>,
|
|
|
|
|
// "line" : <function begin source line in script>,
|
|
|
|
|
// "column" : <function begin source column in script>,
|
|
|
|
|
// "properties" : [ {"name" : <name>,
|
|
|
|
|
// "ref" : <handle>
|
|
|
|
|
// },
|
|
|
|
|
// ...
|
|
|
|
|
// ]
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
QmlV8ObjectData objectData;
|
|
|
|
|
const QVariantMap dataMap = data.toMap();
|
|
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
objectData.name = dataMap.value(NAME).toString();
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2017-03-09 16:17:38 +01:00
|
|
|
QString type = dataMap.value(TYPE).toString();
|
|
|
|
|
objectData.handle = dataMap.value(HANDLE).toInt();
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2017-03-09 16:17:38 +01:00
|
|
|
if (type == "undefined") {
|
|
|
|
|
objectData.type = "undefined";
|
|
|
|
|
objectData.value = "undefined";
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2017-03-09 16:17:38 +01:00
|
|
|
} else if (type == "null") { // Deprecated. typeof(null) == "object" in JavaScript
|
|
|
|
|
objectData.type = "object";
|
|
|
|
|
objectData.value = "null";
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2017-03-09 16:17:38 +01:00
|
|
|
} else if (type == "boolean") {
|
|
|
|
|
objectData.type = "boolean";
|
|
|
|
|
objectData.value = dataMap.value(VALUE);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2017-03-09 16:17:38 +01:00
|
|
|
} else if (type == "number") {
|
|
|
|
|
objectData.type = "number";
|
|
|
|
|
objectData.value = dataMap.value(VALUE);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2017-03-09 16:17:38 +01:00
|
|
|
} else if (type == "string") {
|
2018-10-07 22:38:47 +03:00
|
|
|
QChar quote('"');
|
2017-03-09 16:17:38 +01:00
|
|
|
objectData.type = "string";
|
|
|
|
|
objectData.value = QString(quote + dataMap.value(VALUE).toString() + quote);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2017-03-09 16:17:38 +01:00
|
|
|
} else if (type == "object") {
|
|
|
|
|
objectData.type = "object";
|
|
|
|
|
// ignore "className": it doesn't make any sense.
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2017-03-09 16:17:38 +01:00
|
|
|
if (dataMap.contains("value")) {
|
|
|
|
|
QVariant value = dataMap.value("value");
|
2017-03-20 11:51:18 +01:00
|
|
|
// The QVariant representation of null has changed across various Qt versions
|
|
|
|
|
// 5.6, 5.7: QVariant::Invalid
|
|
|
|
|
// 5.8: isValid(), !isNull(), type() == 51; only typeName() is unique: "std::nullptr_t"
|
|
|
|
|
// 5.9: isValid(), isNull(); We can then use isNull()
|
|
|
|
|
if (!value.isValid() || value.isNull()
|
|
|
|
|
|| strcmp(value.typeName(), "std::nullptr_t") == 0) {
|
2016-06-07 17:04:53 +02:00
|
|
|
objectData.value = "null"; // Yes, null is an object.
|
2017-03-20 11:51:18 +01:00
|
|
|
} else if (value.isValid()) {
|
2017-03-09 16:17:38 +01:00
|
|
|
objectData.expectedProperties = value.toInt();
|
2017-03-20 11:51:18 +01:00
|
|
|
}
|
2017-03-09 16:17:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dataMap.contains("properties"))
|
2016-06-07 17:04:53 +02:00
|
|
|
objectData.properties = dataMap.value("properties").toList();
|
2017-03-09 16:17:38 +01:00
|
|
|
} else if (type == "function") {
|
|
|
|
|
objectData.type = "function";
|
|
|
|
|
objectData.value = dataMap.value(NAME);
|
|
|
|
|
objectData.properties = dataMap.value("properties").toList();
|
|
|
|
|
QVariant value = dataMap.value("value");
|
|
|
|
|
if (value.isValid())
|
|
|
|
|
objectData.expectedProperties = value.toInt();
|
|
|
|
|
|
|
|
|
|
} else if (type == "script") {
|
|
|
|
|
objectData.type = "script";
|
|
|
|
|
objectData.value = dataMap.value(NAME);
|
|
|
|
|
}
|
2016-06-07 17:04:53 +02:00
|
|
|
|
2017-03-09 16:17:38 +01:00
|
|
|
if (dataMap.contains(REF)) {
|
|
|
|
|
objectData.handle = dataMap.value(REF).toInt();
|
|
|
|
|
if (refVals.contains(objectData.handle)) {
|
|
|
|
|
QmlV8ObjectData data = refVals.value(objectData.handle);
|
|
|
|
|
if (objectData.type.isEmpty())
|
|
|
|
|
objectData.type = data.type;
|
|
|
|
|
if (!objectData.value.isValid())
|
|
|
|
|
objectData.value = data.value;
|
|
|
|
|
if (objectData.properties.isEmpty())
|
|
|
|
|
objectData.properties = data.properties;
|
|
|
|
|
if (objectData.expectedProperties < 0)
|
|
|
|
|
objectData.expectedProperties = data.expectedProperties;
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return objectData;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-14 16:24:26 +02:00
|
|
|
void QmlEnginePrivate::runCommand(const DebuggerCommand &command, const QmlCallback &cb)
|
2015-07-08 13:14:03 +02:00
|
|
|
{
|
2015-09-14 13:40:35 +02:00
|
|
|
QJsonObject object;
|
2016-06-07 17:04:53 +02:00
|
|
|
object.insert("seq", ++sequence);
|
|
|
|
|
object.insert("type", "request");
|
|
|
|
|
object.insert("command", command.function);
|
|
|
|
|
object.insert("arguments", command.args);
|
2015-07-14 16:24:26 +02:00
|
|
|
if (cb)
|
|
|
|
|
callbackForToken[sequence] = cb;
|
|
|
|
|
|
2015-09-14 13:40:35 +02:00
|
|
|
runDirectCommand(V8REQUEST, QJsonDocument(object).toJson(QJsonDocument::Compact));
|
2015-07-10 13:06:00 +02:00
|
|
|
}
|
|
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
void QmlEnginePrivate::runDirectCommand(const QString &type, const QByteArray &msg)
|
2015-07-10 13:06:00 +02:00
|
|
|
{
|
|
|
|
|
// Leave item as variable, serialization depends on it.
|
|
|
|
|
QByteArray cmd = V8DEBUG;
|
2011-11-30 15:23:02 +01:00
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
engine->showMessage(QString("%1 %2").arg(type, QString::fromLatin1(msg)), LogInput);
|
2015-07-10 11:06:27 +02:00
|
|
|
|
2017-09-07 15:17:15 +02:00
|
|
|
QPacket rs(dataStreamVersion());
|
2016-06-07 17:04:53 +02:00
|
|
|
rs << cmd << type.toLatin1() << msg;
|
2015-09-17 12:52:50 +02:00
|
|
|
|
|
|
|
|
if (state() == Enabled)
|
2015-11-16 16:46:51 +01:00
|
|
|
sendMessage(rs.data());
|
2015-09-17 12:52:50 +02:00
|
|
|
else
|
2015-11-16 16:46:51 +01:00
|
|
|
sendBuffer.append(rs.data());
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-13 13:36:28 +02:00
|
|
|
void QmlEnginePrivate::memorizeRefs(const QVariant &refs)
|
|
|
|
|
{
|
|
|
|
|
if (refs.isValid()) {
|
2022-04-29 12:43:37 +02:00
|
|
|
const QList<QVariant> refList = refs.toList();
|
|
|
|
|
for (const QVariant &ref : refList) {
|
2015-07-13 13:36:28 +02:00
|
|
|
const QVariantMap refData = ref.toMap();
|
2016-06-07 17:04:53 +02:00
|
|
|
int handle = refData.value(HANDLE).toInt();
|
2015-07-13 13:36:28 +02:00
|
|
|
refVals[handle] = extractData(refData);
|
2015-07-13 13:36:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
void QmlEnginePrivate::messageReceived(const QByteArray &data)
|
|
|
|
|
{
|
2017-09-07 15:17:15 +02:00
|
|
|
QPacket ds(dataStreamVersion(), data);
|
2015-07-08 13:14:03 +02:00
|
|
|
QByteArray command;
|
|
|
|
|
ds >> command;
|
|
|
|
|
|
|
|
|
|
if (command == V8DEBUG) {
|
|
|
|
|
QByteArray type;
|
|
|
|
|
QByteArray response;
|
|
|
|
|
ds >> type >> response;
|
|
|
|
|
|
|
|
|
|
engine->showMessage(QLatin1String(type), LogOutput);
|
|
|
|
|
if (type == CONNECT) {
|
|
|
|
|
//debugging session started
|
|
|
|
|
|
|
|
|
|
} else if (type == INTERRUPT) {
|
|
|
|
|
//debug break requested
|
|
|
|
|
|
|
|
|
|
} else if (type == BREAKONSIGNAL) {
|
|
|
|
|
//break on signal handler requested
|
|
|
|
|
|
|
|
|
|
} else if (type == V8MESSAGE) {
|
2016-06-07 17:04:53 +02:00
|
|
|
SDEBUG(response);
|
|
|
|
|
engine->showMessage(QString(V8MESSAGE) + ' ' + QString::fromLatin1(response), LogOutput);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
const QVariantMap resp = QJsonDocument::fromJson(response).toVariant().toMap();
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
const QString type = resp.value(TYPE).toString();
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
if (type == "response") {
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
const QString debugCommand(resp.value(COMMAND).toString());
|
2015-07-14 16:24:26 +02:00
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
memorizeRefs(resp.value(REFS));
|
2015-07-13 13:36:28 +02:00
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
bool success = resp.value("success").toBool();
|
2015-07-08 13:14:03 +02:00
|
|
|
if (!success) {
|
|
|
|
|
SDEBUG("Request was unsuccessful");
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
int requestSeq = resp.value("request_seq").toInt();
|
2015-07-14 16:24:26 +02:00
|
|
|
if (callbackForToken.contains(requestSeq)) {
|
|
|
|
|
callbackForToken[requestSeq](resp);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
} else if (debugCommand == DISCONNECT) {
|
2015-07-08 13:14:03 +02:00
|
|
|
//debugging session ended
|
|
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
} else if (debugCommand == CONTINEDEBUGGING) {
|
2015-07-08 13:14:03 +02:00
|
|
|
//do nothing, wait for next break
|
|
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
} else if (debugCommand == SETBREAKPOINT) {
|
2015-07-08 13:14:03 +02:00
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "response",
|
|
|
|
|
// "request_seq" : <number>,
|
|
|
|
|
// "command" : "setbreakpoint",
|
|
|
|
|
// "body" : { "type" : <"function" or "script">
|
|
|
|
|
// "breakpoint" : <break point number of the new break point>
|
|
|
|
|
// }
|
|
|
|
|
// "running" : <is the VM running after sending this response>
|
|
|
|
|
// "success" : true
|
|
|
|
|
// }
|
|
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
int seq = resp.value("request_seq").toInt();
|
|
|
|
|
const QVariantMap breakpointData = resp.value(BODY).toMap();
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
const QString index = QString::number(breakpointData.value("breakpoint").toInt());
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
if (breakpointsSync.contains(seq)) {
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
Breakpoint bp = breakpointsSync.take(seq);
|
|
|
|
|
QTC_ASSERT(bp, return);
|
|
|
|
|
bp->setParameters(bp->requestedParameters()); // Assume it worked.
|
|
|
|
|
bp->setResponseId(index);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
//Is actual position info present? Then breakpoint was
|
|
|
|
|
//accepted
|
|
|
|
|
const QVariantList actualLocations =
|
2016-06-07 17:04:53 +02:00
|
|
|
breakpointData.value("actual_locations").toList();
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
const int line = breakpointData.value("line").toInt() + 1;
|
2020-01-15 19:10:34 +01:00
|
|
|
if (!actualLocations.isEmpty()) {
|
2015-07-08 13:14:03 +02:00
|
|
|
//The breakpoint requested line should be same as
|
|
|
|
|
//actual line
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
if (bp && bp->state() != BreakpointInserted) {
|
2023-06-06 15:44:59 +02:00
|
|
|
bp->setTextPosition({line, -1});
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
bp->setPending(false);
|
|
|
|
|
engine->notifyBreakpointInsertOk(bp);
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
breakpointsTemp.append(index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
} else if (debugCommand == CLEARBREAKPOINT) {
|
2015-07-08 13:14:03 +02:00
|
|
|
// DO NOTHING
|
|
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
} else if (debugCommand == SETEXCEPTIONBREAK) {
|
2015-07-08 13:14:03 +02:00
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "response",
|
|
|
|
|
// "request_seq" : <number>,
|
|
|
|
|
// "command" : "setexceptionbreak",
|
|
|
|
|
// "body" : { "type" : <string: "all" or "uncaught" corresponding to the request.>,
|
|
|
|
|
// "enabled" : <bool: true if the break type is currently enabled as a result of the request>
|
|
|
|
|
// }
|
|
|
|
|
// "running" : true
|
|
|
|
|
// "success" : true
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
} else if (debugCommand == SCRIPTS) {
|
2015-07-08 13:14:03 +02:00
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "response",
|
|
|
|
|
// "request_seq" : <number>,
|
|
|
|
|
// "command" : "scripts",
|
|
|
|
|
// "body" : [ { "name" : <name of the script>,
|
|
|
|
|
// "id" : <id of the script>
|
|
|
|
|
// "lineOffset" : <line offset within the containing resource>
|
|
|
|
|
// "columnOffset" : <column offset within the containing resource>
|
|
|
|
|
// "lineCount" : <number of lines in the script>
|
|
|
|
|
// "data" : <optional data object added through the API>
|
|
|
|
|
// "source" : <source of the script if includeSource was specified in the request>
|
|
|
|
|
// "sourceStart" : <first 80 characters of the script if includeSource was not specified in the request>
|
|
|
|
|
// "sourceLength" : <total length of the script in characters>
|
|
|
|
|
// "scriptType" : <script type (see request for values)>
|
|
|
|
|
// "compilationType" : < How was this script compiled:
|
|
|
|
|
// 0 if script was compiled through the API
|
|
|
|
|
// 1 if script was compiled through eval
|
|
|
|
|
// >
|
|
|
|
|
// "evalFromScript" : <if "compilationType" is 1 this is the script from where eval was called>
|
|
|
|
|
// "evalFromLocation" : { line : < if "compilationType" is 1 this is the line in the script from where eval was called>
|
|
|
|
|
// column : < if "compilationType" is 1 this is the column in the script from where eval was called>
|
|
|
|
|
// ]
|
|
|
|
|
// "running" : <is the VM running after sending this response>
|
|
|
|
|
// "success" : true
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
if (success) {
|
2016-06-07 17:04:53 +02:00
|
|
|
const QVariantList body = resp.value(BODY).toList();
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
QStringList sourceFiles;
|
|
|
|
|
for (int i = 0; i < body.size(); ++i) {
|
|
|
|
|
const QVariantMap entryMap = body.at(i).toMap();
|
2016-06-07 17:04:53 +02:00
|
|
|
const int lineOffset = entryMap.value("lineOffset").toInt();
|
|
|
|
|
const int columnOffset = entryMap.value("columnOffset").toInt();
|
|
|
|
|
const QString name = entryMap.value("name").toString();
|
|
|
|
|
const QString source = entryMap.value("source").toString();
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
if (name.isEmpty())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (!sourceFiles.contains(name))
|
|
|
|
|
sourceFiles << name;
|
|
|
|
|
|
|
|
|
|
updateScriptSource(name, lineOffset, columnOffset, source);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-14 18:18:39 +01:00
|
|
|
QMap<QString, FilePath> files;
|
2022-10-07 14:46:06 +02:00
|
|
|
for (const QString &file : std::as_const(sourceFiles)) {
|
2015-07-08 13:14:03 +02:00
|
|
|
QString shortName = file;
|
2023-03-14 18:18:39 +01:00
|
|
|
FilePath fullName = engine->toFileInProject(file);
|
2015-07-08 13:14:03 +02:00
|
|
|
files.insert(shortName, fullName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
engine->sourceFilesHandler()->setSourceFiles(files);
|
|
|
|
|
//update open editors
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// DO NOTHING
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
} else if (type == EVENT) {
|
|
|
|
|
const QString eventType(resp.value(EVENT).toString());
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
if (eventType == "break") {
|
2015-07-13 13:36:28 +02:00
|
|
|
|
|
|
|
|
clearRefs();
|
2016-06-07 17:04:53 +02:00
|
|
|
const QVariantMap breakData = resp.value(BODY).toMap();
|
|
|
|
|
const QString invocationText = breakData.value("invocationText").toString();
|
|
|
|
|
const QString scriptUrl = breakData.value("script").toMap().value("name").toString();
|
|
|
|
|
const QString sourceLineText = breakData.value("sourceLineText").toString();
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
bool inferiorStop = true;
|
|
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
QList<Breakpoint> v8Breakpoints;
|
|
|
|
|
|
|
|
|
|
const QVariantList v8BreakpointIdList = breakData.value("breakpoints").toList();
|
2022-11-18 13:18:16 +01:00
|
|
|
if (engine->state() != InferiorStopRequested) {
|
|
|
|
|
// skip debug break if no breakpoint and we have not done a single step as
|
|
|
|
|
// last action - likely stopped in another file with same naming
|
|
|
|
|
if (v8BreakpointIdList.isEmpty() && previousStepAction == Continue) {
|
|
|
|
|
inferiorStop = false;
|
|
|
|
|
continueDebugging(Continue);
|
|
|
|
|
}
|
2022-06-14 11:34:02 +02:00
|
|
|
}
|
|
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
for (const QVariant &breakpointId : v8BreakpointIdList) {
|
|
|
|
|
const QString responseId = QString::number(breakpointId.toInt());
|
|
|
|
|
Breakpoint bp = engine->breakHandler()->findBreakpointByResponseId(responseId);
|
|
|
|
|
QTC_ASSERT(bp, continue);
|
|
|
|
|
v8Breakpoints << bp;
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
if (!v8Breakpoints.isEmpty() && invocationText.startsWith("[anonymous]()")
|
2016-06-07 17:04:53 +02:00
|
|
|
&& scriptUrl.endsWith(".qml")
|
|
|
|
|
&& sourceLineText.trimmed().startsWith('(')) {
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
// we hit most likely the anonymous wrapper function automatically generated for bindings
|
|
|
|
|
// -> relocate the breakpoint to column: 1 and continue
|
|
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
int newColumn = sourceLineText.indexOf('(') + 1;
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
|
2022-10-07 14:46:06 +02:00
|
|
|
for (const Breakpoint &bp : std::as_const(v8Breakpoints)) {
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
QTC_ASSERT(bp, continue);
|
|
|
|
|
const BreakpointParameters ¶ms = bp->requestedParameters();
|
|
|
|
|
|
|
|
|
|
clearBreakpoint(bp);
|
|
|
|
|
setBreakpoint(SCRIPTREGEXP,
|
2020-01-02 11:31:14 +01:00
|
|
|
params.fileName.toString(),
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
params.enabled,
|
2023-06-06 15:44:59 +02:00
|
|
|
params.textPosition.line,
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
newColumn,
|
|
|
|
|
params.condition,
|
|
|
|
|
params.ignoreCount);
|
|
|
|
|
breakpointsSync.insert(sequence, bp);
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
continueDebugging(Continue);
|
|
|
|
|
inferiorStop = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Skip debug break if this is an internal function
|
2016-06-07 17:04:53 +02:00
|
|
|
if (sourceLineText == INTERNAL_FUNCTION) {
|
2015-07-08 13:14:03 +02:00
|
|
|
continueDebugging(previousStepAction);
|
|
|
|
|
inferiorStop = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (inferiorStop) {
|
|
|
|
|
//Update breakpoint data
|
2022-10-07 14:46:06 +02:00
|
|
|
for (const Breakpoint &bp : std::as_const(v8Breakpoints)) {
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
QTC_ASSERT(bp, continue);
|
|
|
|
|
if (bp->functionName().isEmpty()) {
|
|
|
|
|
bp->setFunctionName(invocationText);
|
|
|
|
|
}
|
|
|
|
|
if (bp->state() != BreakpointInserted) {
|
2023-06-06 15:44:59 +02:00
|
|
|
bp->setTextPosition({breakData.value("sourceLine").toInt() + 1, -1});
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
bp->setPending(false);
|
|
|
|
|
engine->notifyBreakpointInsertOk(bp);
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (engine->state() == InferiorRunOk) {
|
2022-10-07 14:46:06 +02:00
|
|
|
for (const Breakpoint &bp : std::as_const(v8Breakpoints)) {
|
Debugger: Make most views per-engine instead of singletons
This is a step towards properly supporting multiple debugger
sessions side-by-side.
The combined C++-and-QML engine has been removed, instead a
combined setup creates now two individual engines, under a single
DebuggerRunTool but mostly independent with no combined state
machine. This requires a few more clicks in some cases, but
makes it easier to direct e.g. interrupt requests to the
interesting engine.
Care has been taken to not change the UX of the single debugger
session use case if possible. The fat debug button operates
as-before in that case, i.e. switches to Interrupt if the
single active runconfiguration runs in the debugger etc.
Most views are made per-engine, running an engine creates
a new Perspective, which is destroyed when the run control dies.
The snapshot view remains global and becomes primary source
of information on a "current engine" that receives all menu
and otherwise global input.
There is a new global "Breakpoint Preset" view containing
all "static" breakpoint data. When an engine starts up it
"claims" breakpoint it believes it can handle, but operates
on a copy of the static data. The markers of the static
version are suppressed as long as an engine controls a
breakpoint (that inclusive all resolved locations), but are
re-instatet once the engine quits.
The old Breakpoint class that already contained this split
per-instance was split into a new Breakpoint and a
GlobalBreakpoint class, with a per-engine model for Breakpoints,
and a singleton model containing GlobalBreakpoints.
There is a new CppDebuggerEngine intermediate level serving as
base for C++ (or, rather, "compiled") binary debugging, i.e.
{Gdb,Lldb,Cdb}Engine, taking over bits of the current DebuggerEngine
base that are not applicable to non-binary debuggers.
Change-Id: I9994f4c188379b4aee0c4f379edd4759fbb0bd43
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2018-07-31 12:30:48 +02:00
|
|
|
QTC_ASSERT(bp, continue);
|
|
|
|
|
if (breakpointsTemp.contains(bp->responseId()))
|
|
|
|
|
clearBreakpoint(bp);
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
engine->notifyInferiorSpontaneousStop();
|
|
|
|
|
backtrace();
|
2016-07-21 18:03:40 +02:00
|
|
|
} else if (engine->state() == InferiorStopRequested) {
|
|
|
|
|
engine->notifyInferiorStopOk();
|
2015-07-08 13:14:03 +02:00
|
|
|
backtrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
} else if (eventType == "exception") {
|
|
|
|
|
const QVariantMap body = resp.value(BODY).toMap();
|
|
|
|
|
int lineNumber = body.value("sourceLine").toInt() + 1;
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
const QVariantMap script = body.value("script").toMap();
|
|
|
|
|
QUrl fileUrl(script.value(NAME).toString());
|
2023-03-14 18:18:39 +01:00
|
|
|
FilePath filePath = engine->toFileInProject(fileUrl);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
const QVariantMap exception = body.value("exception").toMap();
|
|
|
|
|
QString errorMessage = exception.value("text").toString();
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2018-05-30 15:42:51 +02:00
|
|
|
const QStringList messages = highlightExceptionCode(lineNumber, filePath, errorMessage);
|
|
|
|
|
for (const QString &msg : messages)
|
2015-07-09 15:32:56 +02:00
|
|
|
engine->showMessage(msg, ConsoleOutput);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
if (engine->state() == InferiorRunOk) {
|
|
|
|
|
engine->notifyInferiorSpontaneousStop();
|
|
|
|
|
backtrace();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (engine->state() == InferiorStopOk)
|
|
|
|
|
backtrace();
|
|
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
} else if (eventType == "afterCompile") {
|
2015-07-08 13:14:03 +02:00
|
|
|
//Currently break point relocation is disabled.
|
|
|
|
|
//Uncomment the line below when it will be enabled.
|
|
|
|
|
// listBreakpoints();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Sometimes we do not get event type!
|
|
|
|
|
//This is most probably due to a wrong eval expression.
|
|
|
|
|
//Redirect output to console.
|
|
|
|
|
if (eventType.isEmpty()) {
|
2015-11-10 16:59:02 +01:00
|
|
|
debuggerConsole()->printItem(new ConsoleItem(
|
|
|
|
|
ConsoleItem::ErrorType,
|
2016-06-07 17:04:53 +02:00
|
|
|
resp.value(MESSAGE).toString()));
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} //EVENT
|
|
|
|
|
} //V8MESSAGE
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
//DO NOTHING
|
|
|
|
|
}
|
2012-02-15 12:35:43 +01:00
|
|
|
}
|
|
|
|
|
|
2015-07-14 16:24:26 +02:00
|
|
|
void QmlEnginePrivate::handleBacktrace(const QVariantMap &response)
|
2012-02-28 17:30:15 +01:00
|
|
|
{
|
2015-07-08 13:14:03 +02:00
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "response",
|
|
|
|
|
// "request_seq" : <number>,
|
|
|
|
|
// "command" : "backtrace",
|
|
|
|
|
// "body" : { "fromFrame" : <number>
|
|
|
|
|
// "toFrame" : <number>
|
|
|
|
|
// "totalFrames" : <number>
|
|
|
|
|
// "frames" : <array of frames - see frame request for details>
|
|
|
|
|
// }
|
|
|
|
|
// "running" : <is the VM running after sending this response>
|
|
|
|
|
// "success" : true
|
|
|
|
|
// }
|
|
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
const QVariantMap body = response.value(BODY).toMap();
|
|
|
|
|
const QVariantList frames = body.value("frames").toList();
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
int fromFrameIndex = body.value("fromFrame").toInt();
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
QTC_ASSERT(0 == fromFrameIndex, return);
|
|
|
|
|
|
|
|
|
|
StackHandler *stackHandler = engine->stackHandler();
|
|
|
|
|
StackFrames stackFrames;
|
|
|
|
|
int i = 0;
|
|
|
|
|
stackIndexLookup.clear();
|
2018-05-30 15:42:51 +02:00
|
|
|
for (const QVariant &frame : frames) {
|
2015-07-13 13:36:28 +02:00
|
|
|
StackFrame stackFrame = extractStackFrame(frame);
|
2015-10-08 16:19:57 +02:00
|
|
|
if (stackFrame.level.isEmpty())
|
2015-07-08 13:14:03 +02:00
|
|
|
continue;
|
2015-10-08 16:19:57 +02:00
|
|
|
stackIndexLookup.insert(i, stackFrame.level.toInt());
|
2015-07-08 13:14:03 +02:00
|
|
|
stackFrames << stackFrame;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
stackHandler->setFrames(stackFrames);
|
2015-07-14 16:59:46 +02:00
|
|
|
stackHandler->setCurrentIndex(0);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2015-07-14 16:59:46 +02:00
|
|
|
updateLocals();
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-13 13:36:28 +02:00
|
|
|
StackFrame QmlEnginePrivate::extractStackFrame(const QVariant &bodyVal)
|
2015-07-08 13:14:03 +02:00
|
|
|
{
|
|
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "response",
|
|
|
|
|
// "request_seq" : <number>,
|
|
|
|
|
// "command" : "frame",
|
|
|
|
|
// "body" : { "index" : <frame number>,
|
|
|
|
|
// "receiver" : <frame receiver>,
|
|
|
|
|
// "func" : <function invoked>,
|
|
|
|
|
// "script" : <script for the function>,
|
|
|
|
|
// "constructCall" : <boolean indicating whether the function was called as constructor>,
|
|
|
|
|
// "debuggerFrame" : <boolean indicating whether this is an internal debugger frame>,
|
|
|
|
|
// "arguments" : [ { name: <name of the argument - missing of anonymous argument>,
|
|
|
|
|
// value: <value of the argument>
|
|
|
|
|
// },
|
|
|
|
|
// ... <the array contains all the arguments>
|
|
|
|
|
// ],
|
|
|
|
|
// "locals" : [ { name: <name of the local variable>,
|
|
|
|
|
// value: <value of the local variable>
|
|
|
|
|
// },
|
|
|
|
|
// ... <the array contains all the locals>
|
|
|
|
|
// ],
|
|
|
|
|
// "position" : <source position>,
|
|
|
|
|
// "line" : <source line>,
|
|
|
|
|
// "column" : <source column within the line>,
|
|
|
|
|
// "sourceLineText" : <text for current source line>,
|
|
|
|
|
// "scopes" : [ <array of scopes, see scope request below for format> ],
|
|
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
// "running" : <is the VM running after sending this response>
|
|
|
|
|
// "success" : true
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
const QVariantMap body = bodyVal.toMap();
|
|
|
|
|
|
|
|
|
|
StackFrame stackFrame;
|
2016-06-07 17:04:53 +02:00
|
|
|
stackFrame.level = body.value("index").toString();
|
2015-07-08 13:14:03 +02:00
|
|
|
//Do not insert the frame corresponding to the internal function
|
2016-06-07 17:04:53 +02:00
|
|
|
if (body.value("sourceLineText") == INTERNAL_FUNCTION) {
|
2015-10-08 16:19:57 +02:00
|
|
|
stackFrame.level.clear();
|
2015-07-08 13:14:03 +02:00
|
|
|
return stackFrame;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-09 14:35:48 +01:00
|
|
|
auto extractString = [this](const QVariant &item) {
|
2023-06-09 13:15:11 +02:00
|
|
|
return (item.typeId() == QVariant::String ? item : extractData(item).value).toString();
|
2017-03-09 14:35:48 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
stackFrame.function = extractString(body.value("func"));
|
|
|
|
|
if (stackFrame.function.isEmpty())
|
2023-02-07 22:46:35 +01:00
|
|
|
stackFrame.function = Tr::tr("Anonymous Function");
|
2023-03-14 18:18:39 +01:00
|
|
|
stackFrame.file = engine->toFileInProject(extractString(body.value("script")));
|
2021-10-26 10:48:43 +02:00
|
|
|
stackFrame.usable = stackFrame.file.isReadableFile();
|
2017-03-09 14:35:48 +01:00
|
|
|
stackFrame.receiver = extractString(body.value("receiver"));
|
2016-06-07 17:04:53 +02:00
|
|
|
stackFrame.line = body.value("line").toInt() + 1;
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
return stackFrame;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-14 16:24:26 +02:00
|
|
|
void QmlEnginePrivate::handleFrame(const QVariantMap &response)
|
2015-07-08 13:14:03 +02:00
|
|
|
{
|
|
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "response",
|
|
|
|
|
// "request_seq" : <number>,
|
|
|
|
|
// "command" : "frame",
|
|
|
|
|
// "body" : { "index" : <frame number>,
|
|
|
|
|
// "receiver" : <frame receiver>,
|
|
|
|
|
// "func" : <function invoked>,
|
|
|
|
|
// "script" : <script for the function>,
|
|
|
|
|
// "constructCall" : <boolean indicating whether the function was called as constructor>,
|
|
|
|
|
// "debuggerFrame" : <boolean indicating whether this is an internal debugger frame>,
|
|
|
|
|
// "arguments" : [ { name: <name of the argument - missing of anonymous argument>,
|
|
|
|
|
// value: <value of the argument>
|
|
|
|
|
// },
|
|
|
|
|
// ... <the array contains all the arguments>
|
|
|
|
|
// ],
|
|
|
|
|
// "locals" : [ { name: <name of the local variable>,
|
|
|
|
|
// value: <value of the local variable>
|
|
|
|
|
// },
|
|
|
|
|
// ... <the array contains all the locals>
|
|
|
|
|
// ],
|
|
|
|
|
// "position" : <source position>,
|
|
|
|
|
// "line" : <source line>,
|
|
|
|
|
// "column" : <source column within the line>,
|
|
|
|
|
// "sourceLineText" : <text for current source line>,
|
|
|
|
|
// "scopes" : [ <array of scopes, see scope request below for format> ],
|
|
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
// "running" : <is the VM running after sending this response>
|
|
|
|
|
// "success" : true
|
|
|
|
|
// }
|
2016-06-07 17:04:53 +02:00
|
|
|
QVariantMap body = response.value(BODY).toMap();
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
StackHandler *stackHandler = engine->stackHandler();
|
|
|
|
|
WatchHandler * watchHandler = engine->watchHandler();
|
2016-11-03 13:28:28 +01:00
|
|
|
watchHandler->notifyUpdateStarted();
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
const int frameIndex = stackHandler->currentIndex();
|
|
|
|
|
if (frameIndex < 0)
|
|
|
|
|
return;
|
|
|
|
|
const StackFrame frame = stackHandler->currentFrame();
|
|
|
|
|
if (!frame.isUsable())
|
|
|
|
|
return;
|
|
|
|
|
|
2015-07-14 16:59:46 +02:00
|
|
|
// Always add a "this" variable
|
2015-07-08 13:14:03 +02:00
|
|
|
{
|
2016-06-07 17:04:53 +02:00
|
|
|
QString iname = "local.this";
|
|
|
|
|
QString exp = "this";
|
|
|
|
|
QmlV8ObjectData objectData = extractData(body.value("receiver"));
|
2015-12-16 17:17:38 +01:00
|
|
|
|
|
|
|
|
auto item = new WatchItem;
|
|
|
|
|
item->iname = iname;
|
|
|
|
|
item->name = exp;
|
2015-07-08 13:14:03 +02:00
|
|
|
item->id = objectData.handle;
|
|
|
|
|
item->type = objectData.type;
|
|
|
|
|
item->value = objectData.value.toString();
|
2018-07-09 14:58:44 +02:00
|
|
|
setWatchItemHasChildren(item, objectData.hasChildren());
|
2015-07-08 13:14:03 +02:00
|
|
|
// In case of global object, we do not get children
|
|
|
|
|
// Set children nevertheless and query later.
|
2016-06-07 17:04:53 +02:00
|
|
|
if (item->value == "global") {
|
2018-07-09 14:58:44 +02:00
|
|
|
setWatchItemHasChildren(item, true);
|
2015-07-08 13:14:03 +02:00
|
|
|
item->id = 0;
|
|
|
|
|
}
|
|
|
|
|
watchHandler->insertItem(item);
|
2016-12-07 16:43:12 +01:00
|
|
|
evaluate(exp, -1, [this, iname, exp](const QVariantMap &response) {
|
2015-07-16 14:12:16 +02:00
|
|
|
handleEvaluateExpression(response, iname, exp);
|
2018-05-11 14:09:41 +02:00
|
|
|
|
|
|
|
|
// If there are no scopes, "this" may be the only thing to look up.
|
|
|
|
|
if (currentFrameScopes.isEmpty())
|
|
|
|
|
checkForFinishedUpdate();
|
2015-07-16 14:12:16 +02:00
|
|
|
});
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-14 16:59:46 +02:00
|
|
|
currentFrameScopes.clear();
|
2016-06-07 17:04:53 +02:00
|
|
|
const QVariantList scopes = body.value("scopes").toList();
|
2018-05-30 15:42:51 +02:00
|
|
|
for (const QVariant &scope : scopes) {
|
2015-07-08 13:14:03 +02:00
|
|
|
//Do not query for global types (0)
|
|
|
|
|
//Showing global properties increases clutter.
|
2016-06-07 17:04:53 +02:00
|
|
|
if (scope.toMap().value("type").toInt() == 0)
|
2015-07-08 13:14:03 +02:00
|
|
|
continue;
|
2016-06-07 17:04:53 +02:00
|
|
|
int scopeIndex = scope.toMap().value("index").toInt();
|
2015-07-08 13:14:03 +02:00
|
|
|
currentFrameScopes.append(scopeIndex);
|
|
|
|
|
this->scope(scopeIndex);
|
|
|
|
|
}
|
|
|
|
|
engine->gotoLocation(stackHandler->currentFrame());
|
|
|
|
|
|
2015-07-14 16:59:46 +02:00
|
|
|
// Send watchers list
|
|
|
|
|
if (stackHandler->isContentsValid() && stackHandler->currentFrame().isUsable()) {
|
2020-11-18 22:42:51 +01:00
|
|
|
const QStringList watchers = WatchHandler::watchedExpressions();
|
2018-05-30 15:42:51 +02:00
|
|
|
for (const QString &exp : watchers) {
|
2016-06-07 17:04:53 +02:00
|
|
|
const QString iname = watchHandler->watcherName(exp);
|
2016-12-07 16:43:12 +01:00
|
|
|
evaluate(exp, -1, [this, iname, exp](const QVariantMap &response) {
|
2015-07-16 14:12:16 +02:00
|
|
|
handleEvaluateExpression(response, iname, exp);
|
2015-07-14 16:59:46 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-14 16:24:26 +02:00
|
|
|
void QmlEnginePrivate::handleScope(const QVariantMap &response)
|
2015-07-08 13:14:03 +02:00
|
|
|
{
|
|
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "response",
|
|
|
|
|
// "request_seq" : <number>,
|
|
|
|
|
// "command" : "scope",
|
|
|
|
|
// "body" : { "index" : <index of this scope in the scope chain. Index 0 is the top scope
|
|
|
|
|
// and the global scope will always have the highest index for a
|
|
|
|
|
// frame>,
|
|
|
|
|
// "frameIndex" : <index of the frame>,
|
|
|
|
|
// "type" : <type of the scope:
|
|
|
|
|
// 0: Global
|
|
|
|
|
// 1: Local
|
|
|
|
|
// 2: With
|
|
|
|
|
// 3: Closure
|
|
|
|
|
// 4: Catch >,
|
|
|
|
|
// "object" : <the scope object defining the content of the scope.
|
|
|
|
|
// For local and closure scopes this is transient objects,
|
|
|
|
|
// which has a negative handle value>
|
|
|
|
|
// }
|
|
|
|
|
// "running" : <is the VM running after sending this response>
|
|
|
|
|
// "success" : true
|
|
|
|
|
// }
|
2016-06-07 17:04:53 +02:00
|
|
|
QVariantMap bodyMap = response.value(BODY).toMap();
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
//Check if the frameIndex is same as current Stack Index
|
|
|
|
|
StackHandler *stackHandler = engine->stackHandler();
|
2016-06-07 17:04:53 +02:00
|
|
|
if (bodyMap.value("frameIndex").toInt() != stackHandler->currentIndex())
|
2015-07-08 13:14:03 +02:00
|
|
|
return;
|
|
|
|
|
|
2018-05-30 15:42:51 +02:00
|
|
|
const QmlV8ObjectData objectData = extractData(bodyMap.value("object"));
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2015-07-14 16:59:46 +02:00
|
|
|
LookupItems itemsToLookup;
|
2018-05-30 15:42:51 +02:00
|
|
|
for (const QVariant &property : objectData.properties) {
|
2015-07-13 13:36:28 +02:00
|
|
|
QmlV8ObjectData localData = extractData(property);
|
2016-11-29 13:22:23 +01:00
|
|
|
std::unique_ptr<WatchItem> item(new WatchItem);
|
2015-07-08 13:14:03 +02:00
|
|
|
item->exp = localData.name;
|
|
|
|
|
//Check for v8 specific local data
|
|
|
|
|
if (item->exp.startsWith('.') || item->exp.isEmpty())
|
|
|
|
|
continue;
|
|
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
item->name = item->exp;
|
|
|
|
|
item->iname = "local." + item->exp;
|
2015-07-14 16:59:46 +02:00
|
|
|
item->id = localData.handle;
|
2017-03-09 16:17:38 +01:00
|
|
|
item->type = localData.type;
|
|
|
|
|
item->value = localData.value.toString();
|
2018-07-09 14:58:44 +02:00
|
|
|
setWatchItemHasChildren(item.get(), localData.hasChildren());
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2017-03-09 16:17:38 +01:00
|
|
|
if (localData.value.isValid() || item->wantsChildren || localData.expectedProperties == 0) {
|
2022-10-20 21:52:26 +02:00
|
|
|
WatchHandler *watchHandler = engine->watchHandler();
|
|
|
|
|
if (watchHandler->isExpandedIName(item->iname))
|
2018-07-09 12:42:54 +02:00
|
|
|
itemsToLookup.insert(int(item->id), {item->iname, item->name, item->exp});
|
2022-10-20 21:52:26 +02:00
|
|
|
watchHandler->insertItem(item.release());
|
2012-10-08 13:17:10 +02:00
|
|
|
} else {
|
2015-09-23 13:14:30 +02:00
|
|
|
itemsToLookup.insert(int(item->id), {item->iname, item->name, item->exp});
|
2012-10-08 13:17:10 +02:00
|
|
|
}
|
2012-02-28 17:30:15 +01:00
|
|
|
}
|
2015-07-14 16:59:46 +02:00
|
|
|
lookup(itemsToLookup);
|
|
|
|
|
checkForFinishedUpdate();
|
|
|
|
|
}
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2015-07-14 16:59:46 +02:00
|
|
|
void QmlEnginePrivate::checkForFinishedUpdate()
|
|
|
|
|
{
|
|
|
|
|
if (currentlyLookingUp.isEmpty())
|
2015-07-08 13:14:03 +02:00
|
|
|
engine->watchHandler()->notifyUpdateFinished();
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-21 17:33:53 +02:00
|
|
|
ConsoleItem *QmlEnginePrivate::constructLogItemTree(const QmlV8ObjectData &objectData)
|
2015-07-08 13:14:03 +02:00
|
|
|
{
|
2015-08-21 17:33:53 +02:00
|
|
|
QList<int> handles;
|
|
|
|
|
return constructLogItemTree(objectData, handles);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlEnginePrivate::constructChildLogItems(ConsoleItem *item, const QmlV8ObjectData &objectData,
|
|
|
|
|
QList<int> &seenHandles)
|
|
|
|
|
{
|
|
|
|
|
// We cannot sort the children after attaching them to the parent as that would cause layout
|
|
|
|
|
// changes, invalidating cached indices. So we presort them before inserting.
|
|
|
|
|
QVarLengthArray<ConsoleItem *> children(objectData.properties.size());
|
|
|
|
|
auto it = children.begin();
|
2018-05-30 15:42:51 +02:00
|
|
|
for (const QVariant &property : objectData.properties)
|
2015-08-21 17:33:53 +02:00
|
|
|
*(it++) = constructLogItemTree(extractData(property), seenHandles);
|
|
|
|
|
|
2021-03-01 08:59:44 +01:00
|
|
|
if (debuggerSettings()->sortStructMembers.value())
|
2015-08-21 17:33:53 +02:00
|
|
|
std::sort(children.begin(), children.end(), compareConsoleItems);
|
|
|
|
|
|
2022-10-07 14:46:06 +02:00
|
|
|
for (ConsoleItem *child : std::as_const(children))
|
2015-08-21 17:33:53 +02:00
|
|
|
item->appendChild(child);
|
|
|
|
|
}
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2015-08-21 17:33:53 +02:00
|
|
|
ConsoleItem *QmlEnginePrivate::constructLogItemTree(const QmlV8ObjectData &objectData,
|
|
|
|
|
QList<int> &seenHandles)
|
|
|
|
|
{
|
2015-07-08 13:14:03 +02:00
|
|
|
QString text;
|
2015-08-21 17:33:53 +02:00
|
|
|
if (objectData.value.isValid()) {
|
2015-07-08 13:14:03 +02:00
|
|
|
text = objectData.value.toString();
|
2015-08-21 17:33:53 +02:00
|
|
|
} else if (!objectData.type.isEmpty()) {
|
2016-06-07 17:04:53 +02:00
|
|
|
text = objectData.type;
|
2015-08-21 17:33:53 +02:00
|
|
|
} else {
|
|
|
|
|
int handle = objectData.handle;
|
|
|
|
|
ConsoleItem *item = new ConsoleItem(ConsoleItem::DefaultType,
|
2016-06-07 17:04:53 +02:00
|
|
|
objectData.name,
|
2015-08-21 17:33:53 +02:00
|
|
|
[this, handle](ConsoleItem *item)
|
|
|
|
|
{
|
|
|
|
|
DebuggerCommand cmd(LOOKUP);
|
|
|
|
|
cmd.arg(HANDLES, QList<int>() << handle);
|
|
|
|
|
runCommand(cmd, [this, item, handle](const QVariantMap &response) {
|
2016-06-07 17:04:53 +02:00
|
|
|
const QVariantMap body = response.value(BODY).toMap();
|
2018-05-30 15:42:51 +02:00
|
|
|
const QStringList handlesList = body.keys();
|
|
|
|
|
for (const QString &handleString : handlesList) {
|
2015-08-21 17:33:53 +02:00
|
|
|
if (handle != handleString.toInt())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
QmlV8ObjectData objectData = extractData(body.value(handleString));
|
|
|
|
|
|
|
|
|
|
// keep original name, if possible
|
|
|
|
|
QString name = item->expression();
|
|
|
|
|
if (name.isEmpty())
|
2016-06-07 17:04:53 +02:00
|
|
|
name = objectData.name;
|
2015-08-21 17:33:53 +02:00
|
|
|
|
|
|
|
|
QString value = objectData.value.isValid() ?
|
2016-06-07 17:04:53 +02:00
|
|
|
objectData.value.toString() : objectData.type;
|
2015-08-21 17:33:53 +02:00
|
|
|
|
|
|
|
|
// We can do setData() and cause dataChanged() here, but only because this
|
|
|
|
|
// callback is executed after fetchMore() has returned.
|
|
|
|
|
item->model()->setData(item->index(),
|
2016-06-07 17:04:53 +02:00
|
|
|
QString("%1: %2").arg(name, value),
|
2015-08-21 17:33:53 +02:00
|
|
|
ConsoleItem::ExpressionRole);
|
|
|
|
|
|
|
|
|
|
QList<int> newHandles;
|
|
|
|
|
constructChildLogItems(item, objectData, newHandles);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
return item;
|
|
|
|
|
}
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2015-08-21 17:33:53 +02:00
|
|
|
if (!objectData.name.isEmpty())
|
2016-06-07 17:04:53 +02:00
|
|
|
text = QString("%1: %2").arg(objectData.name, text);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2015-08-21 17:33:53 +02:00
|
|
|
if (objectData.properties.isEmpty())
|
|
|
|
|
return new ConsoleItem(ConsoleItem::DefaultType, text);
|
|
|
|
|
|
|
|
|
|
if (seenHandles.contains(objectData.handle)) {
|
|
|
|
|
ConsoleItem *item = new ConsoleItem(ConsoleItem::DefaultType, text,
|
|
|
|
|
[this, objectData](ConsoleItem *item)
|
|
|
|
|
{
|
|
|
|
|
QList<int> newHandles;
|
|
|
|
|
constructChildLogItems(item, objectData, newHandles);
|
|
|
|
|
});
|
|
|
|
|
return item;
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
|
2015-08-21 17:33:53 +02:00
|
|
|
seenHandles.append(objectData.handle);
|
|
|
|
|
ConsoleItem *item = new ConsoleItem(ConsoleItem::DefaultType, text);
|
|
|
|
|
constructChildLogItems(item, objectData, seenHandles);
|
|
|
|
|
seenHandles.removeLast();
|
|
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
return item;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-13 13:36:28 +02:00
|
|
|
void QmlEnginePrivate::insertSubItems(WatchItem *parent, const QVariantList &properties)
|
2015-07-09 15:32:56 +02:00
|
|
|
{
|
|
|
|
|
QTC_ASSERT(parent, return);
|
2015-08-13 15:29:14 +02:00
|
|
|
LookupItems itemsToLookup;
|
|
|
|
|
|
2018-05-30 15:42:51 +02:00
|
|
|
for (const QVariant &property : properties) {
|
2015-07-13 13:36:28 +02:00
|
|
|
QmlV8ObjectData propertyData = extractData(property);
|
2016-11-29 13:22:23 +01:00
|
|
|
std::unique_ptr<WatchItem> item(new WatchItem);
|
2016-06-07 17:04:53 +02:00
|
|
|
item->name = propertyData.name;
|
2015-07-09 15:32:56 +02:00
|
|
|
|
|
|
|
|
// Check for v8 specific local data
|
2016-06-07 17:04:53 +02:00
|
|
|
if (item->name.startsWith('.') || item->name.isEmpty())
|
2015-07-09 15:32:56 +02:00
|
|
|
continue;
|
|
|
|
|
if (parent->type == "object") {
|
2016-06-07 17:04:53 +02:00
|
|
|
if (parent->value == "Array")
|
|
|
|
|
item->exp = parent->exp + '[' + item->name + ']';
|
|
|
|
|
else if (parent->value == "Object")
|
|
|
|
|
item->exp = parent->exp + '.' + item->name;
|
2015-07-09 15:32:56 +02:00
|
|
|
} else {
|
2016-06-07 17:04:53 +02:00
|
|
|
item->exp = item->name;
|
2015-07-09 15:32:56 +02:00
|
|
|
}
|
|
|
|
|
|
2016-06-07 17:04:53 +02:00
|
|
|
item->iname = parent->iname + '.' + item->name;
|
2015-07-09 15:32:56 +02:00
|
|
|
item->id = propertyData.handle;
|
|
|
|
|
item->type = propertyData.type;
|
|
|
|
|
item->value = propertyData.value.toString();
|
2023-02-21 15:32:13 +01:00
|
|
|
if (item->type.isEmpty() || engine->watchHandler()->isExpandedIName(item->iname))
|
2015-09-23 13:14:30 +02:00
|
|
|
itemsToLookup.insert(propertyData.handle, {item->iname, item->name, item->exp});
|
2018-07-09 14:58:44 +02:00
|
|
|
setWatchItemHasChildren(item.get(), propertyData.hasChildren());
|
2016-11-29 13:22:23 +01:00
|
|
|
parent->appendChild(item.release());
|
2015-07-09 15:32:56 +02:00
|
|
|
}
|
2015-07-15 17:49:04 +02:00
|
|
|
|
2021-03-01 08:59:44 +01:00
|
|
|
if (debuggerSettings()->sortStructMembers.value()) {
|
2016-06-09 15:26:33 +02:00
|
|
|
parent->sortChildren([](const WatchItem *item1, const WatchItem *item2) {
|
|
|
|
|
return item1->name < item2->name;
|
2015-07-15 17:49:04 +02:00
|
|
|
});
|
2015-08-13 15:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lookup(itemsToLookup);
|
2015-07-09 15:32:56 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-14 16:59:46 +02:00
|
|
|
void QmlEnginePrivate::handleExecuteDebuggerCommand(const QVariantMap &response)
|
2015-07-08 13:14:03 +02:00
|
|
|
{
|
2016-06-07 17:04:53 +02:00
|
|
|
auto it = response.constFind(SUCCESS);
|
2015-08-21 17:33:53 +02:00
|
|
|
if (it != response.constEnd() && it.value().toBool()) {
|
2016-06-07 17:04:53 +02:00
|
|
|
debuggerConsole()->printItem(constructLogItemTree(extractData(response.value(BODY))));
|
2015-08-21 17:33:53 +02:00
|
|
|
|
|
|
|
|
// Update the locals
|
2022-10-07 14:46:06 +02:00
|
|
|
for (int index : std::as_const(currentFrameScopes))
|
2015-08-21 17:33:53 +02:00
|
|
|
scope(index);
|
|
|
|
|
} else {
|
2015-11-10 16:59:02 +01:00
|
|
|
debuggerConsole()->printItem(new ConsoleItem(ConsoleItem::ErrorType,
|
2016-06-07 17:04:53 +02:00
|
|
|
response.value(MESSAGE).toString()));
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-14 16:24:26 +02:00
|
|
|
void QmlEnginePrivate::handleLookup(const QVariantMap &response)
|
2015-07-08 13:14:03 +02:00
|
|
|
{
|
|
|
|
|
// { "seq" : <number>,
|
|
|
|
|
// "type" : "response",
|
|
|
|
|
// "request_seq" : <number>,
|
|
|
|
|
// "command" : "lookup",
|
|
|
|
|
// "body" : <array of serialized objects indexed using their handle>
|
|
|
|
|
// "running" : <is the VM running after sending this response>
|
|
|
|
|
// "success" : true
|
|
|
|
|
// }
|
2016-06-07 17:04:53 +02:00
|
|
|
const QVariantMap body = response.value(BODY).toMap();
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2018-05-30 15:42:51 +02:00
|
|
|
const QStringList handlesList = body.keys();
|
|
|
|
|
for (const QString &handleString : handlesList) {
|
|
|
|
|
const int handle = handleString.toInt();
|
|
|
|
|
const QmlV8ObjectData bodyObjectData = extractData(body.value(handleString));
|
2020-07-31 14:11:46 +02:00
|
|
|
const LookupData res = currentlyLookingUp.value(handle);
|
2015-07-14 16:59:46 +02:00
|
|
|
currentlyLookingUp.remove(handle);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2020-07-31 14:11:46 +02:00
|
|
|
auto item = new WatchItem;
|
|
|
|
|
item->exp = res.exp;
|
|
|
|
|
item->iname = res.iname;
|
|
|
|
|
item->name = res.name;
|
|
|
|
|
item->id = handle;
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2020-07-31 14:11:46 +02:00
|
|
|
item->type = bodyObjectData.type;
|
|
|
|
|
item->value = bodyObjectData.value.toString();
|
2015-07-08 13:14:03 +02:00
|
|
|
|
2020-07-31 14:11:46 +02:00
|
|
|
setWatchItemHasChildren(item, bodyObjectData.hasChildren());
|
|
|
|
|
insertSubItems(item, bodyObjectData.properties);
|
|
|
|
|
|
|
|
|
|
engine->watchHandler()->insertItem(item);
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
2015-07-14 16:59:46 +02:00
|
|
|
checkForFinishedUpdate();
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlEnginePrivate::stateChanged(State state)
|
|
|
|
|
{
|
2016-04-13 11:15:06 +02:00
|
|
|
engine->logServiceStateChange(name(), serviceVersion(), state);
|
2015-07-08 13:14:03 +02:00
|
|
|
|
|
|
|
|
if (state == QmlDebugClient::Enabled) {
|
2018-10-09 10:59:46 +02:00
|
|
|
BreakpointManager::claimBreakpointsForEngine(engine);
|
|
|
|
|
|
|
|
|
|
// Since the breakpoint claiming is deferred, we need to also defer the connecting
|
2022-07-19 23:23:34 +02:00
|
|
|
QTimer::singleShot(0, this, [this] {
|
2018-10-09 10:59:46 +02:00
|
|
|
/// Start session.
|
|
|
|
|
flushSendBuffer();
|
|
|
|
|
QJsonObject parameters;
|
|
|
|
|
parameters.insert("redundantRefs", false);
|
|
|
|
|
parameters.insert("namesAsObjects", false);
|
|
|
|
|
runDirectCommand(CONNECT, QJsonDocument(parameters).toJson());
|
|
|
|
|
runCommand({VERSION}, CB(handleVersion));
|
|
|
|
|
});
|
2015-07-08 13:14:03 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-14 16:24:26 +02:00
|
|
|
void QmlEnginePrivate::handleVersion(const QVariantMap &response)
|
|
|
|
|
{
|
2018-07-18 15:41:09 +02:00
|
|
|
const QVariantMap body = response.value(BODY).toMap();
|
|
|
|
|
unpausedEvaluate = body.value("UnpausedEvaluate", false).toBool();
|
|
|
|
|
contextEvaluate = body.value("ContextEvaluate", false).toBool();
|
|
|
|
|
supportChangeBreakpoint = body.value("ChangeBreakpoint", false).toBool();
|
2015-07-14 16:24:26 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-08 13:14:03 +02:00
|
|
|
void QmlEnginePrivate::flushSendBuffer()
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(state() == Enabled, return);
|
2022-10-07 14:46:06 +02:00
|
|
|
for (const QByteArray &msg : std::as_const(sendBuffer))
|
2015-09-17 12:52:50 +02:00
|
|
|
sendMessage(msg);
|
2015-07-08 13:14:03 +02:00
|
|
|
sendBuffer.clear();
|
2012-02-28 17:30:15 +01:00
|
|
|
}
|
|
|
|
|
|
2023-03-14 18:18:39 +01:00
|
|
|
FilePath QmlEngine::toFileInProject(const QUrl &fileUrl)
|
2019-08-26 18:42:51 +02:00
|
|
|
{
|
|
|
|
|
// make sure file finder is properly initialized
|
|
|
|
|
const DebuggerRunParameters &rp = runParameters();
|
|
|
|
|
d->fileFinder.setProjectDirectory(rp.projectSourceDirectory);
|
|
|
|
|
d->fileFinder.setProjectFiles(rp.projectSourceFiles);
|
|
|
|
|
d->fileFinder.setAdditionalSearchDirectories(rp.additionalSearchDirectories);
|
|
|
|
|
d->fileFinder.setSysroot(rp.sysRoot);
|
|
|
|
|
|
2023-03-14 18:18:39 +01:00
|
|
|
return d->fileFinder.findFile(fileUrl).constFirst();
|
2019-08-26 18:42:51 +02:00
|
|
|
}
|
|
|
|
|
|
2017-09-26 16:24:05 +02:00
|
|
|
DebuggerEngine *createQmlEngine()
|
2011-01-12 12:10:12 +01:00
|
|
|
{
|
2017-09-26 16:24:05 +02:00
|
|
|
return new QmlEngine;
|
2011-01-12 12:10:12 +01:00
|
|
|
}
|
|
|
|
|
|
2022-07-05 15:37:08 +02:00
|
|
|
} // Debugger::Internal
|