8#include <private/qv4engine_p.h>
9#include <private/qv4function_p.h>
10#include <private/qqmldebugconnector_p.h>
11#include <private/qversionedpacket_p.h>
13#include <QtCore/QJsonArray>
14#include <QtCore/QJsonDocument>
15#include <QtCore/QJsonObject>
16#include <QtCore/QJsonValue>
23#define NO_PROTOCOL_TRACING
25# define TRACE_PROTOCOL(x)
27#include <QtCore/QDebug>
28# define TRACE_PROTOCOL(x) x
33class V4CommandHandler;
36using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>;
54 TRACE_PROTOCOL(qDebug() <<
"handling command" << command() <<
"...");
57 seq = req.value(QLatin1String(
"seq"));
61 if (!response.isEmpty()) {
62 response[QLatin1String(
"type")] = QStringLiteral(
"response");
63 debugService->send(response);
69 response = QJsonObject();
75 void addCommand() { response.insert(QStringLiteral(
"command"), cmd); }
77 void addSuccess(
bool success) { response.insert(QStringLiteral(
"success"), success); }
80 response.insert(QStringLiteral(
"body"), body);
85 response.insert(QStringLiteral(
"running"), debugService->debuggerAgent.isRunning());
90 QJsonValue command = req.value(QLatin1String(
"command"));
91 response.insert(QStringLiteral(
"command"), command);
95 response.insert(QStringLiteral(
"message"), msg);
99 {
return seq.toInt(-1); }
116 QString msg = QLatin1String(
"unimplemented command \"")
117 + req.value(QLatin1String(
"command")).toString()
119 createErrorResponse(msg);
129 void handleRequest()
override
136 body.insert(QStringLiteral(
"V8Version"),
137 QLatin1String(
"this is not V8, this is V4 in Qt " QT_VERSION_STR));
138 body.insert(QStringLiteral(
"UnpausedEvaluate"),
true);
139 body.insert(QStringLiteral(
"ContextEvaluate"),
true);
140 body.insert(QStringLiteral(
"ChangeBreakpoint"),
true);
150 void handleRequest()
final
153 m_type = QStringLiteral(
"scriptRegExp");
156 m_args = req.value(QLatin1String(
"arguments")).toObject();
157 if (m_args.isEmpty()) {
158 createErrorResponse(QStringLiteral(
"breakpoint request with empty arguments object"));
162 const int id = handleBreakPointRequest();
164 createErrorResponse(m_error);
172 body.insert(QStringLiteral(
"type"), m_type);
173 body.insert(QStringLiteral(
"breakpoint"), id);
179 virtual int handleBreakPointRequest() = 0;
186class V4SetBreakPointRequest:
public V4BreakPointRequest
189 V4SetBreakPointRequest(): V4BreakPointRequest(QStringLiteral(
"setbreakpoint")) {}
191 int handleBreakPointRequest()
final
194 const QString type = m_args.value(QLatin1String(
"type")).toString();
195 if (type != QLatin1String(
"scriptRegExp")) {
196 m_error = QStringLiteral(
"breakpoint type \"%1\" is not implemented").arg(type);
200 const QString fileName = m_args.value(QLatin1String(
"target")).toString();
201 if (fileName.isEmpty()) {
202 m_error = QStringLiteral(
"breakpoint has no file name");
207 const int line = m_args.value(QLatin1String(
"line")).toInt(-1);
209 m_error = QStringLiteral(
"breakpoint has an invalid line number");
213 const bool enabled = m_args.value(QStringLiteral(
"enabled")).toBool(
true);
214 const QString condition = m_args.value(QStringLiteral(
"condition")).toString();
217 return debugService->debuggerAgent.addBreakPoint(fileName, line + 1, enabled, condition);
225class V4ClearBreakPointRequest:
public V4BreakPointRequest
228 V4ClearBreakPointRequest(): V4BreakPointRequest(QStringLiteral(
"clearbreakpoint")) {}
230 int handleBreakPointRequest()
final
232 const int id = m_args.value(QLatin1String(
"breakpoint")).toInt(-1);
234 m_error = QStringLiteral(
"breakpoint has an invalid number");
242class V4ChangeBreakPointRequest:
public V4BreakPointRequest
245 V4ChangeBreakPointRequest(): V4BreakPointRequest(QStringLiteral(
"changebreakpoint")) {}
247 int handleBreakPointRequest()
final
249 const int id = m_args.value(QLatin1String(
"breakpoint")).toInt(-1);
251 m_error = QStringLiteral(
"breakpoint has an invalid number");
255 const QJsonValue enabled = m_args.value(QLatin1String(
"enabled"));
256 if (!enabled.isBool()) {
257 m_error = QStringLiteral(
"missing bool \"enabled\" in breakpoint change request");
262 debugService->debuggerAgent.enableBreakPoint(id, enabled.toBool());
272 void handleRequest()
override
276 QJsonObject arguments = req.value(QLatin1String(
"arguments")).toObject();
277 int fromFrame = arguments.value(QLatin1String(
"fromFrame")).toInt(0);
278 int toFrame = arguments.value(QLatin1String(
"toFrame")).toInt(fromFrame + 10);
283 createErrorResponse(QStringLiteral(
"Debugger has to be paused to retrieve backtraces."));
295 addBody(job.returnValue());
304 void handleRequest()
override
307 QJsonObject arguments = req.value(QLatin1String(
"arguments")).toObject();
308 const int frameNr = arguments.value(QLatin1String(
"number")).toInt(
313 createErrorResponse(QStringLiteral(
"Debugger has to be paused to retrieve frames."));
318 createErrorResponse(QStringLiteral(
"frame command has invalid frame number"));
325 createErrorResponse(QStringLiteral(
"frame retrieval failed"));
336 addBody(job.returnValue());
345 void handleRequest()
override
348 QJsonObject arguments = req.value(QLatin1String(
"arguments")).toObject();
349 const int frameNr = arguments.value(QLatin1String(
"frameNumber")).toInt(
351 const int scopeNr = arguments.value(QLatin1String(
"number")).toInt(0);
355 createErrorResponse(QStringLiteral(
"Debugger has to be paused to retrieve scope."));
360 createErrorResponse(QStringLiteral(
"scope command has invalid frame number"));
364 createErrorResponse(QStringLiteral(
"scope command has invalid scope number"));
371 createErrorResponse(QStringLiteral(
"scope retrieval failed"));
380 addBody(job.returnValue());
389 void handleRequest()
override
392 QJsonObject arguments = req.value(QLatin1String(
"arguments")).toObject();
393 QJsonArray handles = arguments.value(QLatin1String(
"handles")).toArray();
397 const QList<QV4Debugger *> &debuggers =
debugService->debuggerAgent.debuggers();
398 if (debuggers.size() > 1) {
399 createErrorResponse(QStringLiteral(
"Cannot lookup values if multiple debuggers are running and none is paused"));
401 }
else if (debuggers.size() == 0) {
402 createErrorResponse(QStringLiteral(
"No debuggers available to lookup values"));
405 debugger = debuggers.first();
410 if (!job.exceptionMessage().isEmpty()) {
411 createErrorResponse(job.exceptionMessage());
418 addBody(job.returnValue());
428 void handleRequest()
override
431 QJsonObject arguments = req.value(QLatin1String(
"arguments")).toObject();
435 createErrorResponse(QStringLiteral(
"Debugger has to be paused in order to continue."));
440 if (arguments.empty()) {
443 QJsonObject arguments = req.value(QLatin1String(
"arguments")).toObject();
444 QString stepAction = arguments.value(QLatin1String(
"stepaction")).toString();
445 const int stepcount = arguments.value(QLatin1String(
"stepcount")).toInt(1);
447 qWarning() <<
"Step count other than 1 is not supported.";
449 if (stepAction == QLatin1String(
"in")) {
451 }
else if (stepAction == QLatin1String(
"out")) {
453 }
else if (stepAction == QLatin1String(
"next")) {
456 createErrorResponse(QStringLiteral(
"continue command has invalid stepaction"));
474 void handleRequest()
override
490 V4SetExceptionBreakRequest():
V4CommandHandler(QStringLiteral(
"setexceptionbreak")) {}
492 void handleRequest()
override
494 bool wasEnabled =
debugService->debuggerAgent.breakOnThrow();
497 QJsonObject arguments = req.value(QLatin1String(
"arguments")).toObject();
498 QString type = arguments.value(QLatin1String(
"type")).toString();
499 bool enabled = arguments.value(QLatin1String(
"number")).toBool(!wasEnabled);
501 if (type == QLatin1String(
"all")) {
503 }
else if (type == QLatin1String(
"uncaught")) {
504 createErrorResponse(QStringLiteral(
"breaking only on uncaught exceptions is not supported yet"));
507 createErrorResponse(QStringLiteral(
"invalid type for break on exception"));
515 body[QLatin1String(
"type")] = type;
516 body[QLatin1String(
"enabled")] =
debugService->debuggerAgent.breakOnThrow();
532 void handleRequest()
override
535 QJsonObject arguments = req.value(QLatin1String(
"arguments")).toObject();
536 int types = arguments.value(QLatin1String(
"types")).toInt(-1);
537 if (types < 0 || types > 7) {
538 createErrorResponse(QStringLiteral(
"invalid types value in scripts command"));
540 }
else if (types != 4) {
541 createErrorResponse(QStringLiteral(
"unsupported types value in scripts command"));
548 createErrorResponse(QStringLiteral(
"Debugger has to be paused to retrieve scripts."));
556 for (
const QString &source : job.result()) {
558 src[QLatin1String(
"name")] = source;
559 src[QLatin1String(
"scriptType")] = 4;
604 void handleRequest()
override
606 QJsonObject arguments = req.value(QLatin1String(
"arguments")).toObject();
607 QString expression = arguments.value(QLatin1String(
"expression")).toString();
608 int context = arguments.value(QLatin1String(
"context")).toInt(-1);
613 const QList<QV4Debugger *> &debuggers =
debugService->debuggerAgent.debuggers();
614 if (debuggers.size() > 1) {
615 createErrorResponse(QStringLiteral(
"Cannot evaluate expressions if multiple debuggers are running and none is paused"));
617 }
else if (debuggers.size() == 0) {
618 createErrorResponse(QStringLiteral(
"No debuggers available to evaluate expressions"));
621 debugger = debuggers.first();
623 frame = arguments.value(QLatin1String(
"frame")).toInt(0);
630 createErrorResponse(job.exceptionMessage());
636 addBody(job.returnValue());
644 handlers[handler->command()] = handler;
653 return unknownV4CommandHandler.data();
657 QQmlConfigurableDebugService<QV4DebugService>(1, parent),
658 debuggerAgent(
this), theSelectedFrame(0),
659 unknownV4CommandHandler(
new UnknownV4CommandHandler)
661 addHandler(
new V4VersionRequest);
662 addHandler(
new V4SetBreakPointRequest);
663 addHandler(
new V4ClearBreakPointRequest);
664 addHandler(
new V4ChangeBreakPointRequest);
665 addHandler(
new V4BacktraceRequest);
666 addHandler(
new V4FrameRequest);
667 addHandler(
new V4ScopeRequest);
668 addHandler(
new V4LookupRequest);
669 addHandler(
new V4ContinueRequest);
670 addHandler(
new V4DisconnectRequest);
671 addHandler(
new V4SetExceptionBreakRequest);
672 addHandler(
new V4ScriptsRequest);
673 addHandler(
new V4EvaluateRequest);
678 qDeleteAll(handlers);
683 QMutexLocker lock(&m_configMutex);
685 QV4::ExecutionEngine *ee = engine->handle();
686 if (QQmlDebugConnector *server = QQmlDebugConnector::instance()) {
689 if (state() == Enabled)
690 ee->setDebugger(debugger);
691 debuggerAgent.addDebugger(debugger);
692 debuggerAgent.moveToThread(server->thread());
696 QQmlConfigurableDebugService<QV4DebugService>::engineAdded(engine);
701 QMutexLocker lock(&m_configMutex);
703 const QV4::ExecutionEngine *ee = engine->handle();
707 debuggerAgent.removeDebugger(debugger);
710 QQmlConfigurableDebugService<QV4DebugService>::engineAboutToBeRemoved(engine);
715 QMutexLocker lock(&m_configMutex);
716 if (state == Enabled) {
717 const auto debuggers = debuggerAgent.debuggers();
718 for (QV4Debugger *debugger : debuggers) {
719 QV4::ExecutionEngine *ee = debugger->engine();
721 ee->setDebugger(debugger);
724 QQmlConfigurableDebugService<QV4DebugService>::stateAboutToBeChanged(state);
735 QString signalName = signal.left(signal.indexOf(QLatin1Char(
'('))).toLower();
737 for (
const QString &signal : std::as_const(breakOnSignals)) {
738 if (signal == signalName) {
747 QMutexLocker lock(&m_configMutex);
749 QQmlDebugPacket ms(message);
753 TRACE_PROTOCOL(qDebug() <<
"received message with header" << header);
755 if (header ==
"V8DEBUG") {
758 ms >> type >> payload;
761 if (type == V4_CONNECT) {
762 QJsonObject parameters = QJsonDocument::fromJson(payload).object();
763 Q_UNUSED(parameters);
765 emit messageToClient(name(), packMessage(type));
768 debuggerAgent.pauseAll();
773 ms >> signal >> enabled;
775 QString signalName(QString::fromUtf8(signal).toLower());
777 breakOnSignals.append(signalName);
779 breakOnSignals.removeOne(signalName);
780 }
else if (type ==
"v8request") {
781 handleV4Request(payload);
783 TRACE_PROTOCOL(qDebug() <<
"... payload:" << payload.constData());
784 handleV4Request(payload);
794 rs << QByteArray(type)
795 << QByteArray::number(
int(version())) << QByteArray::number(magicNumber);
796 emit messageToClient(name(), packMessage(type, rs.data()));
801 TRACE_PROTOCOL(qDebug() <<
"v8request, payload:" << payload.constData());
803 QJsonDocument request = QJsonDocument::fromJson(payload);
804 QJsonObject o = request.object();
805 QJsonValue type = o.value(QLatin1String(
"type"));
806 if (type.toString() == QLatin1String(
"request")) {
807 QJsonValue command = o.value(QLatin1String(
"command"));
814QByteArray
QV4DebugServiceImpl::packMessage(
const QByteArray &command,
const QByteArray &message)
817 static const QByteArray cmd(
"V8DEBUG");
818 rs << cmd << command << message;
824 v4Payload[QLatin1String(
"seq")] = sequence++;
826 doc.setObject(v4Payload);
828 QByteArray responseData = doc.toJson(QJsonDocument::Compact);
830 QByteArray responseData = doc.toJson(QJsonDocument::Indented);
833 TRACE_PROTOCOL(qDebug() <<
"sending response for:" << responseData.constData() << endl);
835 emit messageToClient(name(), packMessage(
"v8message", responseData));
840 theSelectedFrame = frameNr;
845 return theSelectedFrame;
850#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