9#include <private/qv4engine_p.h>
10#include <private/qv4function_p.h>
11#include <private/qqmldebugconnector_p.h>
12#include <private/qversionedpacket_p.h>
14#include <QtCore/QJsonArray>
15#include <QtCore/QJsonDocument>
16#include <QtCore/QJsonObject>
17#include <QtCore/QJsonValue>
24#define NO_PROTOCOL_TRACING
26# define TRACE_PROTOCOL(x)
28#include <QtCore/QDebug>
29# define TRACE_PROTOCOL(x) x
34class V4CommandHandler;
37using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>;
55 TRACE_PROTOCOL(qDebug() <<
"handling command" << command() <<
"...");
58 seq = req.value(QLatin1String(
"seq"));
62 if (!response.isEmpty()) {
63 response[QLatin1String(
"type")] = QStringLiteral(
"response");
64 debugService->send(response);
70 response = QJsonObject();
76 void addCommand() { response.insert(QStringLiteral(
"command"), cmd); }
78 void addSuccess(
bool success) { response.insert(QStringLiteral(
"success"), success); }
81 response.insert(QStringLiteral(
"body"), body);
86 response.insert(QStringLiteral(
"running"), debugService->debuggerAgent.isRunning());
91 QJsonValue command = req.value(QLatin1String(
"command"));
92 response.insert(QStringLiteral(
"command"), command);
96 response.insert(QStringLiteral(
"message"), msg);
100 {
return seq.toInt(-1); }
117 QString msg = QLatin1String(
"unimplemented command \"")
118 + req.value(QLatin1String(
"command")).toString()
120 createErrorResponse(msg);
130 void handleRequest()
override
137 body.insert(QStringLiteral(
"V8Version"),
138 QLatin1String(
"this is not V8, this is V4 in Qt " QT_VERSION_STR));
139 body.insert(QStringLiteral(
"UnpausedEvaluate"),
true);
140 body.insert(QStringLiteral(
"ContextEvaluate"),
true);
141 body.insert(QStringLiteral(
"ChangeBreakpoint"),
true);
151 void handleRequest()
final
154 m_type = QStringLiteral(
"scriptRegExp");
157 m_args = req.value(QLatin1String(
"arguments")).toObject();
158 if (m_args.isEmpty()) {
159 createErrorResponse(QStringLiteral(
"breakpoint request with empty arguments object"));
163 const int id = handleBreakPointRequest();
165 createErrorResponse(m_error);
173 body.insert(QStringLiteral(
"type"), m_type);
174 body.insert(QStringLiteral(
"breakpoint"), id);
180 virtual int handleBreakPointRequest() = 0;
187class V4SetBreakPointRequest:
public V4BreakPointRequest
190 V4SetBreakPointRequest(): V4BreakPointRequest(QStringLiteral(
"setbreakpoint")) {}
192 int handleBreakPointRequest()
final
195 const QString type = m_args.value(QLatin1String(
"type")).toString();
196 if (type != QLatin1String(
"scriptRegExp")) {
197 m_error = QStringLiteral(
"breakpoint type \"%1\" is not implemented").arg(type);
201 const QString fileName = m_args.value(QLatin1String(
"target")).toString();
202 if (fileName.isEmpty()) {
203 m_error = QStringLiteral(
"breakpoint has no file name");
208 const int line = m_args.value(QLatin1String(
"line")).toInt(-1);
210 m_error = QStringLiteral(
"breakpoint has an invalid line number");
214 const bool enabled = m_args.value(QStringLiteral(
"enabled")).toBool(
true);
215 const QString condition = m_args.value(QStringLiteral(
"condition")).toString();
218 return debugService->debuggerAgent.addBreakPoint(fileName, line + 1, enabled, condition);
226class V4ClearBreakPointRequest:
public V4BreakPointRequest
229 V4ClearBreakPointRequest(): V4BreakPointRequest(QStringLiteral(
"clearbreakpoint")) {}
231 int handleBreakPointRequest()
final
233 const int id = m_args.value(QLatin1String(
"breakpoint")).toInt(-1);
235 m_error = QStringLiteral(
"breakpoint has an invalid number");
243class V4ChangeBreakPointRequest:
public V4BreakPointRequest
246 V4ChangeBreakPointRequest(): V4BreakPointRequest(QStringLiteral(
"changebreakpoint")) {}
248 int handleBreakPointRequest()
final
250 const int id = m_args.value(QLatin1String(
"breakpoint")).toInt(-1);
252 m_error = QStringLiteral(
"breakpoint has an invalid number");
256 const QJsonValue enabled = m_args.value(QLatin1String(
"enabled"));
257 if (!enabled.isBool()) {
258 m_error = QStringLiteral(
"missing bool \"enabled\" in breakpoint change request");
263 debugService->debuggerAgent.enableBreakPoint(id, enabled.toBool());
273 void handleRequest()
override
277 QJsonObject arguments = req.value(QLatin1String(
"arguments")).toObject();
278 int fromFrame = arguments.value(QLatin1String(
"fromFrame")).toInt(0);
279 int toFrame = arguments.value(QLatin1String(
"toFrame")).toInt(fromFrame + 10);
284 createErrorResponse(QStringLiteral(
"Debugger has to be paused to retrieve backtraces."));
296 addBody(job.returnValue());
305 void handleRequest()
override
308 QJsonObject arguments = req.value(QLatin1String(
"arguments")).toObject();
309 const int frameNr = arguments.value(QLatin1String(
"number")).toInt(
314 createErrorResponse(QStringLiteral(
"Debugger has to be paused to retrieve frames."));
319 createErrorResponse(QStringLiteral(
"frame command has invalid frame number"));
326 createErrorResponse(QStringLiteral(
"frame retrieval failed"));
337 addBody(job.returnValue());
346 void handleRequest()
override
349 QJsonObject arguments = req.value(QLatin1String(
"arguments")).toObject();
350 const int frameNr = arguments.value(QLatin1String(
"frameNumber")).toInt(
352 const int scopeNr = arguments.value(QLatin1String(
"number")).toInt(0);
356 createErrorResponse(QStringLiteral(
"Debugger has to be paused to retrieve scope."));
361 createErrorResponse(QStringLiteral(
"scope command has invalid frame number"));
365 createErrorResponse(QStringLiteral(
"scope command has invalid scope number"));
372 createErrorResponse(QStringLiteral(
"scope retrieval failed"));
381 addBody(job.returnValue());
390 void handleRequest()
override
393 QJsonObject arguments = req.value(QLatin1String(
"arguments")).toObject();
394 QJsonArray handles = arguments.value(QLatin1String(
"handles")).toArray();
398 const QList<QV4Debugger *> &debuggers =
debugService->debuggerAgent.debuggers();
399 if (debuggers.size() > 1) {
400 createErrorResponse(QStringLiteral(
"Cannot lookup values if multiple debuggers are running and none is paused"));
402 }
else if (debuggers.size() == 0) {
403 createErrorResponse(QStringLiteral(
"No debuggers available to lookup values"));
406 debugger = debuggers.first();
411 if (!job.exceptionMessage().isEmpty()) {
412 createErrorResponse(job.exceptionMessage());
419 addBody(job.returnValue());
429 void handleRequest()
override
432 QJsonObject arguments = req.value(QLatin1String(
"arguments")).toObject();
436 createErrorResponse(QStringLiteral(
"Debugger has to be paused in order to continue."));
441 if (arguments.empty()) {
444 QJsonObject arguments = req.value(QLatin1String(
"arguments")).toObject();
445 QString stepAction = arguments.value(QLatin1String(
"stepaction")).toString();
446 const int stepcount = arguments.value(QLatin1String(
"stepcount")).toInt(1);
448 qWarning() <<
"Step count other than 1 is not supported.";
450 if (stepAction == QLatin1String(
"in")) {
452 }
else if (stepAction == QLatin1String(
"out")) {
454 }
else if (stepAction == QLatin1String(
"next")) {
457 createErrorResponse(QStringLiteral(
"continue command has invalid stepaction"));
475 void handleRequest()
override
491 V4SetExceptionBreakRequest():
V4CommandHandler(QStringLiteral(
"setexceptionbreak")) {}
493 void handleRequest()
override
495 bool wasEnabled =
debugService->debuggerAgent.breakOnThrow();
498 QJsonObject arguments = req.value(QLatin1String(
"arguments")).toObject();
499 QString type = arguments.value(QLatin1String(
"type")).toString();
500 bool enabled = arguments.value(QLatin1String(
"number")).toBool(!wasEnabled);
502 if (type == QLatin1String(
"all")) {
504 }
else if (type == QLatin1String(
"uncaught")) {
505 createErrorResponse(QStringLiteral(
"breaking only on uncaught exceptions is not supported yet"));
508 createErrorResponse(QStringLiteral(
"invalid type for break on exception"));
516 body[QLatin1String(
"type")] = type;
517 body[QLatin1String(
"enabled")] =
debugService->debuggerAgent.breakOnThrow();
533 void handleRequest()
override
536 QJsonObject arguments = req.value(QLatin1String(
"arguments")).toObject();
537 int types = arguments.value(QLatin1String(
"types")).toInt(-1);
538 if (types < 0 || types > 7) {
539 createErrorResponse(QStringLiteral(
"invalid types value in scripts command"));
541 }
else if (types != 4) {
542 createErrorResponse(QStringLiteral(
"unsupported types value in scripts command"));
549 createErrorResponse(QStringLiteral(
"Debugger has to be paused to retrieve scripts."));
557 for (
const QString &source : job.result()) {
559 src[QLatin1String(
"name")] = source;
560 src[QLatin1String(
"scriptType")] = 4;
605 void handleRequest()
override
607 QJsonObject arguments = req.value(QLatin1String(
"arguments")).toObject();
608 QString expression = arguments.value(QLatin1String(
"expression")).toString();
609 int context = arguments.value(QLatin1String(
"context")).toInt(-1);
614 const QList<QV4Debugger *> &debuggers =
debugService->debuggerAgent.debuggers();
615 if (debuggers.size() > 1) {
616 createErrorResponse(QStringLiteral(
"Cannot evaluate expressions if multiple debuggers are running and none is paused"));
618 }
else if (debuggers.size() == 0) {
619 createErrorResponse(QStringLiteral(
"No debuggers available to evaluate expressions"));
622 debugger = debuggers.first();
624 frame = arguments.value(QLatin1String(
"frame")).toInt(0);
631 createErrorResponse(job.exceptionMessage());
637 addBody(job.returnValue());
645 handlers[handler->command()] = handler;
654 return unknownV4CommandHandler.data();
658 QQmlConfigurableDebugService<QV4DebugService>(1, parent),
659 debuggerAgent(
this), theSelectedFrame(0),
660 unknownV4CommandHandler(
new UnknownV4CommandHandler)
662 addHandler(
new V4VersionRequest);
663 addHandler(
new V4SetBreakPointRequest);
664 addHandler(
new V4ClearBreakPointRequest);
665 addHandler(
new V4ChangeBreakPointRequest);
666 addHandler(
new V4BacktraceRequest);
667 addHandler(
new V4FrameRequest);
668 addHandler(
new V4ScopeRequest);
669 addHandler(
new V4LookupRequest);
670 addHandler(
new V4ContinueRequest);
671 addHandler(
new V4DisconnectRequest);
672 addHandler(
new V4SetExceptionBreakRequest);
673 addHandler(
new V4ScriptsRequest);
674 addHandler(
new V4EvaluateRequest);
679 qDeleteAll(handlers);
684 QMutexLocker lock(&m_configMutex);
686 QV4::ExecutionEngine *ee = engine->handle();
687 if (QQmlDebugConnector *server = QQmlDebugConnector::instance()) {
690 if (state() == Enabled)
691 ee->setDebugger(debugger);
692 debuggerAgent.addDebugger(debugger);
693 debuggerAgent.moveToThread(server->thread());
697 QQmlConfigurableDebugService<QV4DebugService>::engineAdded(engine);
702 QMutexLocker lock(&m_configMutex);
704 const QV4::ExecutionEngine *ee = engine->handle();
708 debuggerAgent.removeDebugger(debugger);
711 QQmlConfigurableDebugService<QV4DebugService>::engineAboutToBeRemoved(engine);
716 QMutexLocker lock(&m_configMutex);
717 if (state == Enabled) {
718 const auto debuggers = debuggerAgent.debuggers();
719 for (QV4Debugger *debugger : debuggers) {
720 QV4::ExecutionEngine *ee = debugger->engine();
722 ee->setDebugger(debugger);
725 QQmlConfigurableDebugService<QV4DebugService>::stateAboutToBeChanged(state);
736 QString signalName = signal.left(signal.indexOf(QLatin1Char(
'('))).toLower();
738 for (
const QString &signal : std::as_const(breakOnSignals)) {
739 if (signal == signalName) {
748 QMutexLocker lock(&m_configMutex);
750 QQmlDebugPacket ms(message);
754 TRACE_PROTOCOL(qDebug() <<
"received message with header" << header);
756 if (header ==
"V8DEBUG") {
759 ms >> type >> payload;
762 if (type == V4_CONNECT) {
763 QJsonObject parameters = QJsonDocument::fromJson(payload).object();
764 Q_UNUSED(parameters);
766 emit messageToClient(name(), packMessage(type));
769 debuggerAgent.pauseAll();
774 ms >> signal >> enabled;
776 QString signalName(QString::fromUtf8(signal).toLower());
778 breakOnSignals.append(signalName);
780 breakOnSignals.removeOne(signalName);
781 }
else if (type ==
"v8request") {
782 handleV4Request(payload);
784 TRACE_PROTOCOL(qDebug() <<
"... payload:" << payload.constData());
785 handleV4Request(payload);
795 rs << QByteArray(type)
796 << QByteArray::number(
int(version())) << QByteArray::number(magicNumber);
797 emit messageToClient(name(), packMessage(type, rs.data()));
802 TRACE_PROTOCOL(qDebug() <<
"v8request, payload:" << payload.constData());
804 QJsonDocument request = QJsonDocument::fromJson(payload);
805 QJsonObject o = request.object();
806 QJsonValue type = o.value(QLatin1String(
"type"));
807 if (type.toString() == QLatin1String(
"request")) {
808 QJsonValue command = o.value(QLatin1String(
"command"));
815QByteArray
QV4DebugServiceImpl::packMessage(
const QByteArray &command,
const QByteArray &message)
818 static const QByteArray cmd(
"V8DEBUG");
819 rs << cmd << command << message;
825 v4Payload[QLatin1String(
"seq")] = sequence++;
827 doc.setObject(v4Payload);
829 QByteArray responseData = doc.toJson(QJsonDocument::Compact);
831 QByteArray responseData = doc.toJson(QJsonDocument::Indented);
834 TRACE_PROTOCOL(qDebug() <<
"sending response for:" << responseData.constData() << endl);
836 emit messageToClient(name(), packMessage(
"v8message", responseData));
841 theSelectedFrame = frameNr;
846 return theSelectedFrame;
851#include "moc_qv4debugservice.cpp"
BacktraceJob(QV4DataCollector *collector, int fromFrame, int toFrame)
bool wasSuccessful() const
FrameJob(QV4DataCollector *collector, int frameNr)
int selectedFrame() const
void send(QJsonObject v4Payload)
void engineAboutToBeRemoved(QJSEngine *engine) override
void messageReceived(const QByteArray &) override
void stateAboutToBeChanged(State state) override
void signalEmitted(const QString &signal) override
void sendSomethingToSomebody(const char *type, int magicNumber=1)
void engineAdded(QJSEngine *engine) override
void selectFrame(int frameNr)
~QV4DebugServiceImpl() override
void runInEngine(QV4DebugJob *job)
QV4DataCollector * collector()
bool wasSuccessful() const
ScopeJob(QV4DataCollector *collector, int frameNr, int scopeNr)
UnknownV4CommandHandler()
void handleRequest() override
virtual ~V4CommandHandler()
QV4DebugServiceImpl * debugService
int requestSequenceNr() const
void addSuccess(bool success)
void createErrorResponse(const QString &msg)
void addBody(const QJsonValue &body)
V4CommandHandler(const QString &command)
virtual void handleRequest()=0
void handle(const QJsonObject &request, QV4DebugServiceImpl *s)
void addRequestSequence()
const char *const V4_CONNECT
const char *const V4_BREAK_ON_SIGNAL
#define NO_PROTOCOL_TRACING
const char *const V4_PAUSE
#define TRACE_PROTOCOL(x)
const char *const V4_DISCONNECT