1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qv4debuggeragent.h"
5#include "qv4debugservice.h"
6#include "qv4datacollector.h"
7
8#include <private/qv4stackframe_p.h>
9
10#include <QtCore/qjsonobject.h>
11#include <QtCore/qjsonarray.h>
12
13QT_BEGIN_NAMESPACE
14
15QV4Debugger *QV4DebuggerAgent::pausedDebugger() const
16{
17 for (QV4Debugger *debugger : m_debuggers) {
18 if (debugger->state() == QV4Debugger::Paused)
19 return debugger;
20 }
21 return nullptr;
22}
23
24bool QV4DebuggerAgent::isRunning() const
25{
26 // "running" means none of the engines are paused.
27 return pausedDebugger() == nullptr;
28}
29
30void QV4DebuggerAgent::debuggerPaused(QV4Debugger *debugger, QV4Debugger::PauseReason reason)
31{
32 Q_UNUSED(reason);
33
34 debugger->collector()->clear();
35
36 QJsonObject event, body, script;
37 event.insert(QStringLiteral("type"), QStringLiteral("event"));
38
39 switch (reason) {
40 case QV4Debugger::Step:
41 case QV4Debugger::PauseRequest:
42 case QV4Debugger::BreakPointHit: {
43 event.insert(QStringLiteral("event"), QStringLiteral("break"));
44 QV4::CppStackFrame *frame = debugger->engine()->currentStackFrame;
45 if (!frame)
46 break;
47
48 body.insert(QStringLiteral("invocationText"), value: frame->function());
49 body.insert(QStringLiteral("sourceLine"), value: qAbs(t: frame->lineNumber()) - 1);
50// if (frame->column > 0)
51// body.insert(QStringLiteral("sourceColumn"), frame->column);
52 QJsonArray breakPoints;
53 const QList<int> ids = breakPointIds(fileName: frame->source(), lineNumber: frame->lineNumber());
54 for (int breakPointId : ids)
55 breakPoints.push_back(t: breakPointId);
56 body.insert(QStringLiteral("breakpoints"), value: breakPoints);
57 script.insert(QStringLiteral("name"), value: frame->source());
58 } break;
59 case QV4Debugger::Throwing:
60 // TODO: complete this!
61 event.insert(QStringLiteral("event"), QStringLiteral("exception"));
62 break;
63 }
64
65 if (!script.isEmpty())
66 body.insert(QStringLiteral("script"), value: script);
67 if (!body.isEmpty())
68 event.insert(QStringLiteral("body"), value: body);
69 m_debugService->send(v4Payload: event);
70}
71
72void QV4DebuggerAgent::addDebugger(QV4Debugger *debugger)
73{
74 Q_ASSERT(!m_debuggers.contains(debugger));
75 m_debuggers << debugger;
76
77 debugger->setBreakOnThrow(m_breakOnThrow);
78
79 for (const BreakPoint &breakPoint : std::as_const(t&: m_breakPoints))
80 if (breakPoint.enabled)
81 debugger->addBreakPoint(fileName: breakPoint.fileName, lineNumber: breakPoint.lineNr, condition: breakPoint.condition);
82
83 connect(sender: debugger, signal: &QObject::destroyed, context: this, slot: &QV4DebuggerAgent::handleDebuggerDeleted);
84 connect(sender: debugger, signal: &QV4Debugger::debuggerPaused, context: this, slot: &QV4DebuggerAgent::debuggerPaused,
85 type: Qt::QueuedConnection);
86}
87
88void QV4DebuggerAgent::removeDebugger(QV4Debugger *debugger)
89{
90 m_debuggers.removeAll(t: debugger);
91 disconnect(sender: debugger, signal: &QObject::destroyed, receiver: this, slot: &QV4DebuggerAgent::handleDebuggerDeleted);
92 disconnect(sender: debugger, signal: &QV4Debugger::debuggerPaused, receiver: this, slot: &QV4DebuggerAgent::debuggerPaused);
93}
94
95const QList<QV4Debugger *> &QV4DebuggerAgent::debuggers()
96{
97 return m_debuggers;
98}
99
100void QV4DebuggerAgent::handleDebuggerDeleted(QObject *debugger)
101{
102 m_debuggers.removeAll(t: static_cast<QV4Debugger *>(debugger));
103}
104
105void QV4DebuggerAgent::pause(QV4Debugger *debugger) const
106{
107 debugger->pause();
108}
109
110void QV4DebuggerAgent::pauseAll() const
111{
112 for (QV4Debugger *debugger : m_debuggers)
113 pause(debugger);
114}
115
116void QV4DebuggerAgent::resumeAll() const
117{
118 for (QV4Debugger *debugger : m_debuggers)
119 if (debugger->state() == QV4Debugger::Paused)
120 debugger->resume(speed: QV4Debugger::FullThrottle);
121}
122
123int QV4DebuggerAgent::addBreakPoint(const QString &fileName, int lineNumber, bool enabled, const QString &condition)
124{
125 if (enabled) {
126 for (QV4Debugger *debugger : std::as_const(t&: m_debuggers))
127 debugger->addBreakPoint(fileName, lineNumber, condition);
128 }
129
130 const int id = ++m_lastBreakPointId;
131 m_breakPoints.insert(key: id, value: BreakPoint(fileName, lineNumber, enabled, condition));
132 return id;
133}
134
135void QV4DebuggerAgent::removeBreakPoint(int id)
136{
137 BreakPoint breakPoint = m_breakPoints.value(key: id);
138 if (!breakPoint.isValid())
139 return;
140
141 m_breakPoints.remove(key: id);
142
143 if (breakPoint.enabled)
144 for (QV4Debugger *debugger : std::as_const(t&: m_debuggers))
145 debugger->removeBreakPoint(fileName: breakPoint.fileName, lineNumber: breakPoint.lineNr);
146}
147
148void QV4DebuggerAgent::removeAllBreakPoints()
149{
150 for (auto it = m_breakPoints.keyBegin(), end = m_breakPoints.keyEnd(); it != end; ++it)
151 removeBreakPoint(id: *it);
152}
153
154void QV4DebuggerAgent::enableBreakPoint(int id, bool onoff)
155{
156 BreakPoint &breakPoint = m_breakPoints[id];
157 if (!breakPoint.isValid() || breakPoint.enabled == onoff)
158 return;
159 breakPoint.enabled = onoff;
160
161 for (QV4Debugger *debugger : std::as_const(t&: m_debuggers)) {
162 if (onoff)
163 debugger->addBreakPoint(fileName: breakPoint.fileName, lineNumber: breakPoint.lineNr, condition: breakPoint.condition);
164 else
165 debugger->removeBreakPoint(fileName: breakPoint.fileName, lineNumber: breakPoint.lineNr);
166 }
167}
168
169QList<int> QV4DebuggerAgent::breakPointIds(const QString &fileName, int lineNumber) const
170{
171 QList<int> ids;
172
173 for (QHash<int, BreakPoint>::const_iterator i = m_breakPoints.begin(), ei = m_breakPoints.end(); i != ei; ++i)
174 if (i->lineNr == lineNumber && fileName.endsWith(s: i->fileName))
175 ids.push_back(t: i.key());
176
177 return ids;
178}
179
180void QV4DebuggerAgent::setBreakOnThrow(bool onoff)
181{
182 if (onoff != m_breakOnThrow) {
183 m_breakOnThrow = onoff;
184 for (QV4Debugger *debugger : std::as_const(t&: m_debuggers))
185 debugger->setBreakOnThrow(onoff);
186 }
187}
188
189void QV4DebuggerAgent::clearAllPauseRequests()
190{
191 for (QV4Debugger *debugger : std::as_const(t&: m_debuggers))
192 debugger->clearPauseRequest();
193}
194
195QT_END_NAMESPACE
196
197#include "moc_qv4debuggeragent.cpp"
198

source code of qtdeclarative/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp