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

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