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 "qv4debugservice.h"
5#include "qv4debugjob.h"
6#include "qqmlengine.h"
7
8#include <private/qv4engine_p.h>
9#include <private/qv4function_p.h>
10#include <private/qqmldebugconnector_p.h>
11#include <private/qversionedpacket_p.h>
12
13#include <QtCore/QJsonArray>
14#include <QtCore/QJsonDocument>
15#include <QtCore/QJsonObject>
16#include <QtCore/QJsonValue>
17
18const char *const V4_CONNECT = "connect";
19const char *const V4_DISCONNECT = "disconnect";
20const char *const V4_BREAK_ON_SIGNAL = "breakonsignal";
21const char *const V4_PAUSE = "interrupt";
22
23#define NO_PROTOCOL_TRACING
24#ifdef NO_PROTOCOL_TRACING
25# define TRACE_PROTOCOL(x)
26#else
27#include <QtCore/QDebug>
28# define TRACE_PROTOCOL(x) x
29#endif
30
31QT_BEGIN_NAMESPACE
32
33class V4CommandHandler;
34class UnknownV4CommandHandler;
35
36using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>;
37
38int QV4DebugServiceImpl::sequence = 0;
39
40class V4CommandHandler
41{
42public:
43 V4CommandHandler(const QString &command)
44 : cmd(command)
45 {}
46
47 virtual ~V4CommandHandler()
48 {}
49
50 QString command() const { return cmd; }
51
52 void handle(const QJsonObject &request, QV4DebugServiceImpl *s)
53 {
54 TRACE_PROTOCOL(qDebug() << "handling command" << command() << "...");
55
56 req = request;
57 seq = req.value(key: QLatin1String("seq"));
58 debugService = s;
59
60 handleRequest();
61 if (!response.isEmpty()) {
62 response[QLatin1String("type")] = QStringLiteral("response");
63 debugService->send(v4Payload: response);
64 }
65
66 debugService = nullptr;
67 seq = QJsonValue();
68 req = QJsonObject();
69 response = QJsonObject();
70 }
71
72 virtual void handleRequest() = 0;
73
74protected:
75 void addCommand() { response.insert(QStringLiteral("command"), value: cmd); }
76 void addRequestSequence() { response.insert(QStringLiteral("request_seq"), value: seq); }
77 void addSuccess(bool success) { response.insert(QStringLiteral("success"), value: success); }
78 void addBody(const QJsonValue &body)
79 {
80 response.insert(QStringLiteral("body"), value: body);
81 }
82
83 void addRunning()
84 {
85 response.insert(QStringLiteral("running"), value: debugService->debuggerAgent.isRunning());
86 }
87
88 void createErrorResponse(const QString &msg)
89 {
90 QJsonValue command = req.value(key: QLatin1String("command"));
91 response.insert(QStringLiteral("command"), value: command);
92 addRequestSequence();
93 addSuccess(success: false);
94 addRunning();
95 response.insert(QStringLiteral("message"), value: msg);
96 }
97
98 int requestSequenceNr() const
99 { return seq.toInt(defaultValue: -1); }
100
101protected:
102 QString cmd;
103 QJsonObject req;
104 QJsonValue seq;
105 QV4DebugServiceImpl *debugService;
106 QJsonObject response;
107};
108
109class UnknownV4CommandHandler: public V4CommandHandler
110{
111public:
112 UnknownV4CommandHandler(): V4CommandHandler(QString()) {}
113
114 void handleRequest() override
115 {
116 QString msg = QLatin1String("unimplemented command \"")
117 + req.value(key: QLatin1String("command")).toString()
118 + QLatin1Char('"');
119 createErrorResponse(msg);
120 }
121};
122
123namespace {
124class V4VersionRequest: public V4CommandHandler
125{
126public:
127 V4VersionRequest(): V4CommandHandler(QStringLiteral("version")) {}
128
129 void handleRequest() override
130 {
131 addCommand();
132 addRequestSequence();
133 addSuccess(success: true);
134 addRunning();
135 QJsonObject body;
136 body.insert(QStringLiteral("V8Version"),
137 value: QLatin1String("this is not V8, this is V4 in Qt " QT_VERSION_STR));
138 body.insert(QStringLiteral("UnpausedEvaluate"), value: true);
139 body.insert(QStringLiteral("ContextEvaluate"), value: true);
140 body.insert(QStringLiteral("ChangeBreakpoint"), value: true);
141 addBody(body);
142 }
143};
144
145class V4BreakPointRequest: public V4CommandHandler
146{
147public:
148 V4BreakPointRequest(const QString &name): V4CommandHandler(name) {}
149
150 void handleRequest() final
151 {
152 // Other types are currently not supported
153 m_type = QStringLiteral("scriptRegExp");
154
155 // decypher the payload:
156 m_args = req.value(key: QLatin1String("arguments")).toObject();
157 if (m_args.isEmpty()) {
158 createErrorResponse(QStringLiteral("breakpoint request with empty arguments object"));
159 return;
160 }
161
162 const int id = handleBreakPointRequest();
163 if (id < 0) {
164 createErrorResponse(msg: m_error);
165 } else {
166 // response:
167 addCommand();
168 addRequestSequence();
169 addSuccess(success: true);
170 addRunning();
171 QJsonObject body;
172 body.insert(QStringLiteral("type"), value: m_type);
173 body.insert(QStringLiteral("breakpoint"), value: id);
174 addBody(body);
175 }
176 }
177
178protected:
179 virtual int handleBreakPointRequest() = 0;
180
181 QJsonObject m_args;
182 QString m_type;
183 QString m_error;
184};
185
186class V4SetBreakPointRequest: public V4BreakPointRequest
187{
188public:
189 V4SetBreakPointRequest(): V4BreakPointRequest(QStringLiteral("setbreakpoint")) {}
190
191 int handleBreakPointRequest() final
192 {
193 // decypher the payload:
194 const QString type = m_args.value(key: QLatin1String("type")).toString();
195 if (type != QLatin1String("scriptRegExp")) {
196 m_error = QStringLiteral("breakpoint type \"%1\" is not implemented").arg(a: type);
197 return -1;
198 }
199
200 const QString fileName = m_args.value(key: QLatin1String("target")).toString();
201 if (fileName.isEmpty()) {
202 m_error = QStringLiteral("breakpoint has no file name");
203 return -1;
204 }
205
206
207 const int line = m_args.value(key: QLatin1String("line")).toInt(defaultValue: -1);
208 if (line < 0) {
209 m_error = QStringLiteral("breakpoint has an invalid line number");
210 return -1;
211 }
212
213 const bool enabled = m_args.value(QStringLiteral("enabled")).toBool(defaultValue: true);
214 const QString condition = m_args.value(QStringLiteral("condition")).toString();
215
216 // set the break point:
217 return debugService->debuggerAgent.addBreakPoint(fileName, lineNumber: line + 1, enabled, condition);
218
219 // It's undocumented, but V8 sends back an actual_locations array too. However, our
220 // Debugger currently doesn't tell us when it resolved a breakpoint, so we'll leave them
221 // pending until the breakpoint is hit for the first time.
222 }
223};
224
225class V4ClearBreakPointRequest: public V4BreakPointRequest
226{
227public:
228 V4ClearBreakPointRequest(): V4BreakPointRequest(QStringLiteral("clearbreakpoint")) {}
229
230 int handleBreakPointRequest() final
231 {
232 const int id = m_args.value(key: QLatin1String("breakpoint")).toInt(defaultValue: -1);
233 if (id < 0)
234 m_error = QStringLiteral("breakpoint has an invalid number");
235 else // remove the break point:
236 debugService->debuggerAgent.removeBreakPoint(id);
237
238 return id;
239 }
240};
241
242class V4ChangeBreakPointRequest: public V4BreakPointRequest
243{
244public:
245 V4ChangeBreakPointRequest(): V4BreakPointRequest(QStringLiteral("changebreakpoint")) {}
246
247 int handleBreakPointRequest() final
248 {
249 const int id = m_args.value(key: QLatin1String("breakpoint")).toInt(defaultValue: -1);
250 if (id < 0) {
251 m_error = QStringLiteral("breakpoint has an invalid number");
252 return id;
253 }
254
255 const QJsonValue enabled = m_args.value(key: QLatin1String("enabled"));
256 if (!enabled.isBool()) {
257 m_error = QStringLiteral("missing bool \"enabled\" in breakpoint change request");
258 return -1;
259 }
260
261 // enable or disable the break point:
262 debugService->debuggerAgent.enableBreakPoint(id, onoff: enabled.toBool());
263 return id;
264 }
265};
266
267class V4BacktraceRequest: public V4CommandHandler
268{
269public:
270 V4BacktraceRequest(): V4CommandHandler(QStringLiteral("backtrace")) {}
271
272 void handleRequest() override
273 {
274 // decypher the payload:
275
276 QJsonObject arguments = req.value(key: QLatin1String("arguments")).toObject();
277 int fromFrame = arguments.value(key: QLatin1String("fromFrame")).toInt(defaultValue: 0);
278 int toFrame = arguments.value(key: QLatin1String("toFrame")).toInt(defaultValue: fromFrame + 10);
279 // no idea what the bottom property is for, so we'll ignore it.
280
281 QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
282 if (!debugger) {
283 createErrorResponse(QStringLiteral("Debugger has to be paused to retrieve backtraces."));
284 return;
285 }
286
287 BacktraceJob job(debugger->collector(), fromFrame, toFrame);
288 debugger->runInEngine(job: &job);
289
290 // response:
291 addCommand();
292 addRequestSequence();
293 addSuccess(success: true);
294 addRunning();
295 addBody(body: job.returnValue());
296 }
297};
298
299class V4FrameRequest: public V4CommandHandler
300{
301public:
302 V4FrameRequest(): V4CommandHandler(QStringLiteral("frame")) {}
303
304 void handleRequest() override
305 {
306 // decypher the payload:
307 QJsonObject arguments = req.value(key: QLatin1String("arguments")).toObject();
308 const int frameNr = arguments.value(key: QLatin1String("number")).toInt(
309 defaultValue: debugService->selectedFrame());
310
311 QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
312 if (!debugger) {
313 createErrorResponse(QStringLiteral("Debugger has to be paused to retrieve frames."));
314 return;
315 }
316
317 if (frameNr < 0) {
318 createErrorResponse(QStringLiteral("frame command has invalid frame number"));
319 return;
320 }
321
322 FrameJob job(debugger->collector(), frameNr);
323 debugger->runInEngine(job: &job);
324 if (!job.wasSuccessful()) {
325 createErrorResponse(QStringLiteral("frame retrieval failed"));
326 return;
327 }
328
329 debugService->selectFrame(frameNr);
330
331 // response:
332 addCommand();
333 addRequestSequence();
334 addSuccess(success: true);
335 addRunning();
336 addBody(body: job.returnValue());
337 }
338};
339
340class V4ScopeRequest: public V4CommandHandler
341{
342public:
343 V4ScopeRequest(): V4CommandHandler(QStringLiteral("scope")) {}
344
345 void handleRequest() override
346 {
347 // decypher the payload:
348 QJsonObject arguments = req.value(key: QLatin1String("arguments")).toObject();
349 const int frameNr = arguments.value(key: QLatin1String("frameNumber")).toInt(
350 defaultValue: debugService->selectedFrame());
351 const int scopeNr = arguments.value(key: QLatin1String("number")).toInt(defaultValue: 0);
352
353 QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
354 if (!debugger) {
355 createErrorResponse(QStringLiteral("Debugger has to be paused to retrieve scope."));
356 return;
357 }
358
359 if (frameNr < 0) {
360 createErrorResponse(QStringLiteral("scope command has invalid frame number"));
361 return;
362 }
363 if (scopeNr < 0) {
364 createErrorResponse(QStringLiteral("scope command has invalid scope number"));
365 return;
366 }
367
368 ScopeJob job(debugger->collector(), frameNr, scopeNr);
369 debugger->runInEngine(job: &job);
370 if (!job.wasSuccessful()) {
371 createErrorResponse(QStringLiteral("scope retrieval failed"));
372 return;
373 }
374
375 // response:
376 addCommand();
377 addRequestSequence();
378 addSuccess(success: true);
379 addRunning();
380 addBody(body: job.returnValue());
381 }
382};
383
384class V4LookupRequest: public V4CommandHandler
385{
386public:
387 V4LookupRequest(): V4CommandHandler(QStringLiteral("lookup")) {}
388
389 void handleRequest() override
390 {
391 // decypher the payload:
392 QJsonObject arguments = req.value(key: QLatin1String("arguments")).toObject();
393 QJsonArray handles = arguments.value(key: QLatin1String("handles")).toArray();
394
395 QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
396 if (!debugger) {
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"));
400 return;
401 } else if (debuggers.size() == 0) {
402 createErrorResponse(QStringLiteral("No debuggers available to lookup values"));
403 return;
404 }
405 debugger = debuggers.first();
406 }
407
408 ValueLookupJob job(handles, debugger->collector());
409 debugger->runInEngine(job: &job);
410 if (!job.exceptionMessage().isEmpty()) {
411 createErrorResponse(msg: job.exceptionMessage());
412 } else {
413 // response:
414 addCommand();
415 addRequestSequence();
416 addSuccess(success: true);
417 addRunning();
418 addBody(body: job.returnValue());
419 }
420 }
421};
422
423class V4ContinueRequest: public V4CommandHandler
424{
425public:
426 V4ContinueRequest(): V4CommandHandler(QStringLiteral("continue")) {}
427
428 void handleRequest() override
429 {
430 // decypher the payload:
431 QJsonObject arguments = req.value(key: QLatin1String("arguments")).toObject();
432
433 QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
434 if (!debugger) {
435 createErrorResponse(QStringLiteral("Debugger has to be paused in order to continue."));
436 return;
437 }
438 debugService->debuggerAgent.clearAllPauseRequests();
439
440 if (arguments.empty()) {
441 debugger->resume(speed: QV4Debugger::FullThrottle);
442 } else {
443 QJsonObject arguments = req.value(key: QLatin1String("arguments")).toObject();
444 QString stepAction = arguments.value(key: QLatin1String("stepaction")).toString();
445 const int stepcount = arguments.value(key: QLatin1String("stepcount")).toInt(defaultValue: 1);
446 if (stepcount != 1)
447 qWarning() << "Step count other than 1 is not supported.";
448
449 if (stepAction == QLatin1String("in")) {
450 debugger->resume(speed: QV4Debugger::StepIn);
451 } else if (stepAction == QLatin1String("out")) {
452 debugger->resume(speed: QV4Debugger::StepOut);
453 } else if (stepAction == QLatin1String("next")) {
454 debugger->resume(speed: QV4Debugger::StepOver);
455 } else {
456 createErrorResponse(QStringLiteral("continue command has invalid stepaction"));
457 return;
458 }
459 }
460
461 // response:
462 addCommand();
463 addRequestSequence();
464 addSuccess(success: true);
465 addRunning();
466 }
467};
468
469class V4DisconnectRequest: public V4CommandHandler
470{
471public:
472 V4DisconnectRequest(): V4CommandHandler(QStringLiteral("disconnect")) {}
473
474 void handleRequest() override
475 {
476 debugService->debuggerAgent.removeAllBreakPoints();
477 debugService->debuggerAgent.resumeAll();
478
479 // response:
480 addCommand();
481 addRequestSequence();
482 addSuccess(success: true);
483 addRunning();
484 }
485};
486
487class V4SetExceptionBreakRequest: public V4CommandHandler
488{
489public:
490 V4SetExceptionBreakRequest(): V4CommandHandler(QStringLiteral("setexceptionbreak")) {}
491
492 void handleRequest() override
493 {
494 bool wasEnabled = debugService->debuggerAgent.breakOnThrow();
495
496 //decypher the payload:
497 QJsonObject arguments = req.value(key: QLatin1String("arguments")).toObject();
498 QString type = arguments.value(key: QLatin1String("type")).toString();
499 bool enabled = arguments.value(key: QLatin1String("number")).toBool(defaultValue: !wasEnabled);
500
501 if (type == QLatin1String("all")) {
502 // that's fine
503 } else if (type == QLatin1String("uncaught")) {
504 createErrorResponse(QStringLiteral("breaking only on uncaught exceptions is not supported yet"));
505 return;
506 } else {
507 createErrorResponse(QStringLiteral("invalid type for break on exception"));
508 return;
509 }
510
511 // do it:
512 debugService->debuggerAgent.setBreakOnThrow(enabled);
513
514 QJsonObject body;
515 body[QLatin1String("type")] = type;
516 body[QLatin1String("enabled")] = debugService->debuggerAgent.breakOnThrow();
517
518 // response:
519 addBody(body);
520 addRunning();
521 addSuccess(success: true);
522 addRequestSequence();
523 addCommand();
524 }
525};
526
527class V4ScriptsRequest: public V4CommandHandler
528{
529public:
530 V4ScriptsRequest(): V4CommandHandler(QStringLiteral("scripts")) {}
531
532 void handleRequest() override
533 {
534 //decypher the payload:
535 QJsonObject arguments = req.value(key: QLatin1String("arguments")).toObject();
536 int types = arguments.value(key: QLatin1String("types")).toInt(defaultValue: -1);
537 if (types < 0 || types > 7) {
538 createErrorResponse(QStringLiteral("invalid types value in scripts command"));
539 return;
540 } else if (types != 4) {
541 createErrorResponse(QStringLiteral("unsupported types value in scripts command"));
542 return;
543 }
544
545 // do it:
546 QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
547 if (!debugger) {
548 createErrorResponse(QStringLiteral("Debugger has to be paused to retrieve scripts."));
549 return;
550 }
551
552 GatherSourcesJob job(debugger->engine());
553 debugger->runInEngine(job: &job);
554
555 QJsonArray body;
556 for (const QString &source : job.result()) {
557 QJsonObject src;
558 src[QLatin1String("name")] = source;
559 src[QLatin1String("scriptType")] = 4;
560 body.append(value: src);
561 }
562
563 addSuccess(success: true);
564 addRunning();
565 addBody(body);
566 addCommand();
567 addRequestSequence();
568 }
569};
570
571// Request:
572// {
573// "seq": 4,
574// "type": "request",
575// "command": "evaluate",
576// "arguments": {
577// "expression": "a",
578// "frame": 0
579// }
580// }
581//
582// Response:
583// {
584// "body": {
585// "handle": 3,
586// "type": "number",
587// "value": 1
588// },
589// "command": "evaluate",
590// "refs": [],
591// "request_seq": 4,
592// "running": false,
593// "seq": 5,
594// "success": true,
595// "type": "response"
596// }
597//
598// The "value" key in "body" is the result of evaluating the expression in the request.
599class V4EvaluateRequest: public V4CommandHandler
600{
601public:
602 V4EvaluateRequest(): V4CommandHandler(QStringLiteral("evaluate")) {}
603
604 void handleRequest() override
605 {
606 QJsonObject arguments = req.value(key: QLatin1String("arguments")).toObject();
607 QString expression = arguments.value(key: QLatin1String("expression")).toString();
608 int context = arguments.value(key: QLatin1String("context")).toInt(defaultValue: -1);
609 int frame = -1;
610
611 QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
612 if (!debugger) {
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"));
616 return;
617 } else if (debuggers.size() == 0) {
618 createErrorResponse(QStringLiteral("No debuggers available to evaluate expressions"));
619 return;
620 }
621 debugger = debuggers.first();
622 } else {
623 frame = arguments.value(key: QLatin1String("frame")).toInt(defaultValue: 0);
624 }
625
626 ExpressionEvalJob job(debugger->engine(), frame, context, expression,
627 debugger->collector());
628 debugger->runInEngine(job: &job);
629 if (job.hasExeption()) {
630 createErrorResponse(msg: job.exceptionMessage());
631 } else {
632 addCommand();
633 addRequestSequence();
634 addSuccess(success: true);
635 addRunning();
636 addBody(body: job.returnValue());
637 }
638 }
639};
640} // anonymous namespace
641
642void QV4DebugServiceImpl::addHandler(V4CommandHandler* handler)
643{
644 handlers[handler->command()] = handler;
645}
646
647V4CommandHandler *QV4DebugServiceImpl::v4CommandHandler(const QString &command) const
648{
649 V4CommandHandler *handler = handlers.value(key: command, defaultValue: 0);
650 if (handler)
651 return handler;
652 else
653 return unknownV4CommandHandler.data();
654}
655
656QV4DebugServiceImpl::QV4DebugServiceImpl(QObject *parent) :
657 QQmlConfigurableDebugService<QV4DebugService>(1, parent),
658 debuggerAgent(this), theSelectedFrame(0),
659 unknownV4CommandHandler(new UnknownV4CommandHandler)
660{
661 addHandler(handler: new V4VersionRequest);
662 addHandler(handler: new V4SetBreakPointRequest);
663 addHandler(handler: new V4ClearBreakPointRequest);
664 addHandler(handler: new V4ChangeBreakPointRequest);
665 addHandler(handler: new V4BacktraceRequest);
666 addHandler(handler: new V4FrameRequest);
667 addHandler(handler: new V4ScopeRequest);
668 addHandler(handler: new V4LookupRequest);
669 addHandler(handler: new V4ContinueRequest);
670 addHandler(handler: new V4DisconnectRequest);
671 addHandler(handler: new V4SetExceptionBreakRequest);
672 addHandler(handler: new V4ScriptsRequest);
673 addHandler(handler: new V4EvaluateRequest);
674}
675
676QV4DebugServiceImpl::~QV4DebugServiceImpl()
677{
678 qDeleteAll(c: handlers);
679}
680
681void QV4DebugServiceImpl::engineAdded(QJSEngine *engine)
682{
683 QMutexLocker lock(&m_configMutex);
684 if (engine) {
685 QV4::ExecutionEngine *ee = engine->handle();
686 if (QQmlDebugConnector *server = QQmlDebugConnector::instance()) {
687 if (ee) {
688 QV4Debugger *debugger = new QV4Debugger(ee);
689 if (state() == Enabled)
690 ee->setDebugger(debugger);
691 debuggerAgent.addDebugger(debugger);
692 debuggerAgent.moveToThread(thread: server->thread());
693 }
694 }
695 }
696 QQmlConfigurableDebugService<QV4DebugService>::engineAdded(engine);
697}
698
699void QV4DebugServiceImpl::engineAboutToBeRemoved(QJSEngine *engine)
700{
701 QMutexLocker lock(&m_configMutex);
702 if (engine){
703 const QV4::ExecutionEngine *ee = engine->handle();
704 if (ee) {
705 QV4Debugger *debugger = qobject_cast<QV4Debugger *>(object: ee->debugger());
706 if (debugger)
707 debuggerAgent.removeDebugger(debugger);
708 }
709 }
710 QQmlConfigurableDebugService<QV4DebugService>::engineAboutToBeRemoved(engine);
711}
712
713void QV4DebugServiceImpl::stateAboutToBeChanged(State state)
714{
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();
720 if (!ee->debugger())
721 ee->setDebugger(debugger);
722 }
723 }
724 QQmlConfigurableDebugService<QV4DebugService>::stateAboutToBeChanged(state);
725}
726
727void QV4DebugServiceImpl::signalEmitted(const QString &signal)
728{
729 //This function is only called by QQmlBoundSignal
730 //only if there is a slot connected to the signal. Hence, there
731 //is no need for additional check.
732
733 //Parse just the name and remove the class info
734 //Normalize to Lower case.
735 QString signalName = signal.left(n: signal.indexOf(c: QLatin1Char('('))).toLower();
736
737 for (const QString &signal : std::as_const(t&: breakOnSignals)) {
738 if (signal == signalName) {
739 // TODO: pause debugger
740 break;
741 }
742 }
743}
744
745void QV4DebugServiceImpl::messageReceived(const QByteArray &message)
746{
747 QMutexLocker lock(&m_configMutex);
748
749 QQmlDebugPacket ms(message);
750 QByteArray header;
751 ms >> header;
752
753 TRACE_PROTOCOL(qDebug() << "received message with header" << header);
754
755 if (header == "V8DEBUG") {
756 QByteArray type;
757 QByteArray payload;
758 ms >> type >> payload;
759 TRACE_PROTOCOL(qDebug() << "... type:" << type);
760
761 if (type == V4_CONNECT) {
762 QJsonObject parameters = QJsonDocument::fromJson(json: payload).object();
763 Q_UNUSED(parameters); // For future protocol changes
764
765 emit messageToClient(name: name(), message: packMessage(command: type));
766 stopWaiting();
767 } else if (type == V4_PAUSE) {
768 debuggerAgent.pauseAll();
769 sendSomethingToSomebody(type);
770 } else if (type == V4_BREAK_ON_SIGNAL) {
771 QByteArray signal;
772 bool enabled;
773 ms >> signal >> enabled;
774 //Normalize to lower case.
775 QString signalName(QString::fromUtf8(ba: signal).toLower());
776 if (enabled)
777 breakOnSignals.append(t: signalName);
778 else
779 breakOnSignals.removeOne(t: signalName);
780 } else if (type == "v8request") {
781 handleV4Request(payload);
782 } else if (type == V4_DISCONNECT) {
783 TRACE_PROTOCOL(qDebug() << "... payload:" << payload.constData());
784 handleV4Request(payload);
785 } else {
786 sendSomethingToSomebody(type, magicNumber: 0);
787 }
788 }
789}
790
791void QV4DebugServiceImpl::sendSomethingToSomebody(const char *type, int magicNumber)
792{
793 QQmlDebugPacket rs;
794 rs << QByteArray(type)
795 << QByteArray::number(int(version())) << QByteArray::number(magicNumber);
796 emit messageToClient(name: name(), message: packMessage(command: type, message: rs.data()));
797}
798
799void QV4DebugServiceImpl::handleV4Request(const QByteArray &payload)
800{
801 TRACE_PROTOCOL(qDebug() << "v8request, payload:" << payload.constData());
802
803 QJsonDocument request = QJsonDocument::fromJson(json: payload);
804 QJsonObject o = request.object();
805 QJsonValue type = o.value(key: QLatin1String("type"));
806 if (type.toString() == QLatin1String("request")) {
807 QJsonValue command = o.value(key: QLatin1String("command"));
808 V4CommandHandler *h = v4CommandHandler(command: command.toString());
809 if (h)
810 h->handle(request: o, s: this);
811 }
812}
813
814QByteArray QV4DebugServiceImpl::packMessage(const QByteArray &command, const QByteArray &message)
815{
816 QQmlDebugPacket rs;
817 static const QByteArray cmd("V8DEBUG");
818 rs << cmd << command << message;
819 return rs.data();
820}
821
822void QV4DebugServiceImpl::send(QJsonObject v4Payload)
823{
824 v4Payload[QLatin1String("seq")] = sequence++;
825 QJsonDocument doc;
826 doc.setObject(v4Payload);
827#ifdef NO_PROTOCOL_TRACING
828 QByteArray responseData = doc.toJson(format: QJsonDocument::Compact);
829#else
830 QByteArray responseData = doc.toJson(QJsonDocument::Indented);
831#endif
832
833 TRACE_PROTOCOL(qDebug() << "sending response for:" << responseData.constData() << endl);
834
835 emit messageToClient(name: name(), message: packMessage(command: "v8message", message: responseData));
836}
837
838void QV4DebugServiceImpl::selectFrame(int frameNr)
839{
840 theSelectedFrame = frameNr;
841}
842
843int QV4DebugServiceImpl::selectedFrame() const
844{
845 return theSelectedFrame;
846}
847
848QT_END_NAMESPACE
849
850#include "moc_qv4debugservice.cpp"
851

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