1/****************************************************************************
2**
3** Copyright (C) 2018 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 "qv4debugclient_p.h"
41#include "qv4debugclient_p_p.h"
42#include "qqmldebugconnection_p.h"
43
44#include <private/qpacket_p.h>
45
46#include <QJsonDocument>
47#include <QJsonObject>
48#include <QJsonValue>
49#include <QJsonArray>
50
51QT_BEGIN_NAMESPACE
52
53const char *V8REQUEST = "v8request";
54const char *V8MESSAGE = "v8message";
55const char *SEQ = "seq";
56const char *TYPE = "type";
57const char *COMMAND = "command";
58const char *ARGUMENTS = "arguments";
59const char *STEPACTION = "stepaction";
60const char *STEPCOUNT = "stepcount";
61const char *EXPRESSION = "expression";
62const char *FRAME = "frame";
63const char *CONTEXT = "context";
64const char *GLOBAL = "global";
65const char *DISABLEBREAK = "disable_break";
66const char *HANDLES = "handles";
67const char *INCLUDESOURCE = "includeSource";
68const char *FROMFRAME = "fromFrame";
69const char *TOFRAME = "toFrame";
70const char *BOTTOM = "bottom";
71const char *NUMBER = "number";
72const char *FRAMENUMBER = "frameNumber";
73const char *TYPES = "types";
74const char *IDS = "ids";
75const char *FILTER = "filter";
76const char *FROMLINE = "fromLine";
77const char *TOLINE = "toLine";
78const char *TARGET = "target";
79const char *LINE = "line";
80const char *COLUMN = "column";
81const char *ENABLED = "enabled";
82const char *CONDITION = "condition";
83const char *IGNORECOUNT = "ignoreCount";
84const char *BREAKPOINT = "breakpoint";
85const char *FLAGS = "flags";
86
87const char *CONTINEDEBUGGING = "continue";
88const char *EVALUATE = "evaluate";
89const char *LOOKUP = "lookup";
90const char *BACKTRACE = "backtrace";
91const char *SCOPE = "scope";
92const char *SCOPES = "scopes";
93const char *SCRIPTS = "scripts";
94const char *SOURCE = "source";
95const char *SETBREAKPOINT = "setbreakpoint";
96const char *CLEARBREAKPOINT = "clearbreakpoint";
97const char *CHANGEBREAKPOINT = "changebreakpoint";
98const char *SETEXCEPTIONBREAK = "setexceptionbreak";
99const char *VERSION = "version";
100const char *DISCONNECT = "disconnect";
101const char *GARBAGECOLLECTOR = "gc";
102
103const char *CONNECT = "connect";
104const char *INTERRUPT = "interrupt";
105
106const char *REQUEST = "request";
107const char *IN = "in";
108const char *NEXT = "next";
109const char *OUT = "out";
110
111const char *SCRIPT = "script";
112const char *SCRIPTREGEXP = "scriptRegExp";
113const char *EVENT = "event";
114
115const char *ALL = "all";
116const char *UNCAUGHT = "uncaught";
117
118#define VARIANTMAPINIT \
119 Q_D(QV4DebugClient); \
120 QJsonObject jsonVal; \
121 jsonVal.insert(QLatin1String(SEQ), d->seq++); \
122 jsonVal.insert(QLatin1String(TYPE), QLatin1String(REQUEST));
123
124QV4DebugClient::QV4DebugClient(QQmlDebugConnection *connection)
125 : QQmlDebugClient(*new QV4DebugClientPrivate(connection))
126{
127 QObject::connect(sender: this, signal: &QQmlDebugClient::stateChanged,
128 context: this, slot: [this](State state) { d_func()->onStateChanged(state); });
129}
130
131QV4DebugClientPrivate::QV4DebugClientPrivate(QQmlDebugConnection *connection) :
132 QQmlDebugClientPrivate(QLatin1String("V8Debugger"), connection)
133{
134}
135
136void QV4DebugClient::connect()
137{
138 Q_D(QV4DebugClient);
139 d->sendMessage(command: CONNECT);
140}
141
142void QV4DebugClient::interrupt()
143{
144 Q_D(QV4DebugClient);
145 d->sendMessage(command: INTERRUPT);
146}
147
148void QV4DebugClient::continueDebugging(StepAction action)
149{
150 // { "seq" : <number>,
151 // "type" : "request",
152 // "command" : "continue",
153 // "arguments" : { "stepaction" : <"in", "next" or "out">,
154 // "stepcount" : <number of steps (default 1)>
155 // }
156 // }
157 VARIANTMAPINIT;
158 jsonVal.insert(key: QLatin1String(COMMAND), value: QLatin1String(CONTINEDEBUGGING));
159
160 if (action != Continue) {
161 QJsonObject args;
162 switch (action) {
163 case In:
164 args.insert(key: QLatin1String(STEPACTION), value: QLatin1String(IN));
165 break;
166 case Out:
167 args.insert(key: QLatin1String(STEPACTION), value: QLatin1String(OUT));
168 break;
169 case Next:
170 args.insert(key: QLatin1String(STEPACTION), value: QLatin1String(NEXT));
171 break;
172 default:
173 break;
174 }
175 jsonVal.insert(key: QLatin1String(ARGUMENTS), value: args);
176 }
177
178 d->sendMessage(command: V8REQUEST, args: jsonVal);
179}
180
181void QV4DebugClient::evaluate(const QString &expr, int frame, int context)
182{
183 // { "seq" : <number>,
184 // "type" : "request",
185 // "command" : "evaluate",
186 // "arguments" : { "expression" : <expression to evaluate>,
187 // "frame" : <number>,
188 // "context" : <object ID>
189 // }
190 // }
191 VARIANTMAPINIT;
192 jsonVal.insert(key: QLatin1String(COMMAND), value: QLatin1String(EVALUATE));
193
194 QJsonObject args;
195 args.insert(key: QLatin1String(EXPRESSION), value: expr);
196
197 if (frame != -1)
198 args.insert(key: QLatin1String(FRAME), value: frame);
199
200 if (context != -1)
201 args.insert(key: QLatin1String(CONTEXT), value: context);
202
203 jsonVal.insert(key: QLatin1String(ARGUMENTS), value: args);
204
205 d->sendMessage(command: V8REQUEST, args: jsonVal);
206}
207
208void QV4DebugClient::lookup(const QList<int> &handles, bool includeSource)
209{
210 // { "seq" : <number>,
211 // "type" : "request",
212 // "command" : "lookup",
213 // "arguments" : { "handles" : <array of handles>,
214 // "includeSource" : <boolean indicating whether the source will be included when script objects are returned>,
215 // }
216 // }
217 VARIANTMAPINIT;
218 jsonVal.insert(key: QLatin1String(COMMAND),value: (QLatin1String(LOOKUP)));
219
220 QJsonObject args;
221 QJsonArray array;
222
223 for (int handle : handles)
224 array.append(value: handle);
225
226 args.insert(key: QLatin1String(HANDLES), value: array);
227
228 if (includeSource)
229 args.insert(key: QLatin1String(INCLUDESOURCE), value: includeSource);
230
231 jsonVal.insert(key: QLatin1String(ARGUMENTS), value: args);
232
233 d->sendMessage(command: V8REQUEST, args: jsonVal);
234}
235
236void QV4DebugClient::backtrace(int fromFrame, int toFrame, bool bottom)
237{
238 // { "seq" : <number>,
239 // "type" : "request",
240 // "command" : "backtrace",
241 // "arguments" : { "fromFrame" : <number>
242 // "toFrame" : <number>
243 // "bottom" : <boolean, set to true if the bottom of the stack is requested>
244 // }
245 // }
246 VARIANTMAPINIT;
247 jsonVal.insert(key: QLatin1String(COMMAND), value: QLatin1String(BACKTRACE));
248
249 QJsonObject args;
250
251 if (fromFrame != -1)
252 args.insert(key: QLatin1String(FROMFRAME), value: fromFrame);
253
254 if (toFrame != -1)
255 args.insert(key: QLatin1String(TOFRAME), value: toFrame);
256
257 if (bottom)
258 args.insert(key: QLatin1String(BOTTOM), value: bottom);
259
260 jsonVal.insert(key: QLatin1String(ARGUMENTS), value: args);
261 d->sendMessage(command: V8REQUEST, args: jsonVal);
262}
263
264void QV4DebugClient::frame(int number)
265{
266 // { "seq" : <number>,
267 // "type" : "request",
268 // "command" : "frame",
269 // "arguments" : { "number" : <frame number>
270 // }
271 // }
272 VARIANTMAPINIT;
273 jsonVal.insert(key: QLatin1String(COMMAND), value: QLatin1String(FRAME));
274
275 if (number != -1) {
276 QJsonObject args;
277 args.insert(key: QLatin1String(NUMBER), value: number);
278 jsonVal.insert(key: QLatin1String(ARGUMENTS), value: args);
279 }
280
281 d->sendMessage(command: V8REQUEST, args: jsonVal);
282}
283
284void QV4DebugClient::scope(int number, int frameNumber)
285{
286 // { "seq" : <number>,
287 // "type" : "request",
288 // "command" : "scope",
289 // "arguments" : { "number" : <scope number>
290 // "frameNumber" : <frame number, optional uses selected frame if missing>
291 // }
292 // }
293 VARIANTMAPINIT;
294 jsonVal.insert(key: QLatin1String(COMMAND), value: QLatin1String(SCOPE));
295
296 if (number != -1) {
297 QJsonObject args;
298 args.insert(key: QLatin1String(NUMBER), value: number);
299
300 if (frameNumber != -1)
301 args.insert(key: QLatin1String(FRAMENUMBER), value: frameNumber);
302
303 jsonVal.insert(key: QLatin1String(ARGUMENTS), value: args);
304 }
305
306 d->sendMessage(command: V8REQUEST, args: jsonVal);
307}
308
309void QV4DebugClient::scripts(int types, const QList<int> &ids, bool includeSource)
310{
311 // { "seq" : <number>,
312 // "type" : "request",
313 // "command" : "scripts",
314 // "arguments" : { "types" : <types of scripts to retrieve
315 // set bit 0 for native scripts
316 // set bit 1 for extension scripts
317 // set bit 2 for normal scripts
318 // (default is 4 for normal scripts)>
319 // "ids" : <array of id's of scripts to return. If this is not specified all scripts are requrned>
320 // "includeSource" : <boolean indicating whether the source code should be included for the scripts returned>
321 // "filter" : <string or number: filter string or script id.
322 // If a number is specified, then only the script with the same number as its script id will be retrieved.
323 // If a string is specified, then only scripts whose names contain the filter string will be retrieved.>
324 // }
325 // }
326 VARIANTMAPINIT;
327 jsonVal.insert(key: QLatin1String(COMMAND), value: QLatin1String(SCRIPTS));
328
329 QJsonObject args;
330 args.insert(key: QLatin1String(TYPES), value: types);
331
332 if (ids.count()) {
333 QJsonArray array;
334 for (int id : ids)
335 array.append(value: id);
336
337 args.insert(key: QLatin1String(IDS), value: array);
338 }
339
340 if (includeSource)
341 args.insert(key: QLatin1String(INCLUDESOURCE), value: includeSource);
342
343 jsonVal.insert(key: QLatin1String(ARGUMENTS), value: args);
344 d->sendMessage(command: V8REQUEST, args: jsonVal);
345}
346
347void QV4DebugClient::setBreakpoint(const QString &target, int line, int column, bool enabled,
348 const QString &condition, int ignoreCount)
349{
350 // { "seq" : <number>,
351 // "type" : "request",
352 // "command" : "setbreakpoint",
353 // "arguments" : { "type" : "scriptRegExp"
354 // "target" : <function expression or script identification>
355 // "line" : <line in script or function>
356 // "column" : <character position within the line>
357 // "enabled" : <initial enabled state. True or false, default is true>
358 // "condition" : <string with break point condition>
359 // "ignoreCount" : <number specifying the number of break point hits to ignore, default value is 0>
360 // }
361 // }
362
363 VARIANTMAPINIT;
364 jsonVal.insert(key: QLatin1String(COMMAND), value: QLatin1String(SETBREAKPOINT));
365
366 QJsonObject args;
367
368 args.insert(key: QLatin1String(TYPE), value: QLatin1String(SCRIPTREGEXP));
369 args.insert(key: QLatin1String(TARGET), value: target);
370
371 if (line != -1)
372 args.insert(key: QLatin1String(LINE), value: line);
373
374 if (column != -1)
375 args.insert(key: QLatin1String(COLUMN), value: column);
376
377 args.insert(key: QLatin1String(ENABLED), value: enabled);
378
379 if (!condition.isEmpty())
380 args.insert(key: QLatin1String(CONDITION), value: condition);
381
382 if (ignoreCount != -1)
383 args.insert(key: QLatin1String(IGNORECOUNT), value: ignoreCount);
384
385 jsonVal.insert(key: QLatin1String(ARGUMENTS),value: args);
386 d->sendMessage(command: V8REQUEST, args: jsonVal);
387}
388
389void QV4DebugClient::clearBreakpoint(int breakpoint)
390{
391 // { "seq" : <number>,
392 // "type" : "request",
393 // "command" : "clearbreakpoint",
394 // "arguments" : { "breakpoint" : <number of the break point to clear>
395 // }
396 // }
397 VARIANTMAPINIT;
398 jsonVal.insert(key: QLatin1String(COMMAND), value: QLatin1String(CLEARBREAKPOINT));
399
400 QJsonObject args;
401 args.insert(key: QLatin1String(BREAKPOINT), value: breakpoint);
402 jsonVal.insert(key: QLatin1String(ARGUMENTS),value: args);
403
404 d->sendMessage(command: V8REQUEST, args: jsonVal);
405}
406
407void QV4DebugClient::changeBreakpoint(int breakpoint, bool enabled)
408{
409 // { "seq" : <number>,
410 // "type" : "request",
411 // "command" : "changebreakpoint",
412 // "arguments" : { "breakpoint" : <number of the break point to change>
413 // "enabled" : <bool: enables the break type if true, disables if false>
414 // }
415 // }
416 VARIANTMAPINIT;
417 jsonVal.insert(key: QLatin1String(COMMAND), value: QLatin1String(CHANGEBREAKPOINT));
418
419 QJsonObject args;
420 args.insert(key: QLatin1String(BREAKPOINT), value: breakpoint);
421 args.insert(key: QLatin1String(ENABLED), value: enabled);
422
423 jsonVal.insert(key: QLatin1String(ARGUMENTS), value: args);
424 d->sendMessage(command: V8REQUEST, args: jsonVal);
425}
426
427void QV4DebugClient::setExceptionBreak(Exception type, bool enabled)
428{
429 // { "seq" : <number>,
430 // "type" : "request",
431 // "command" : "setexceptionbreak",
432 // "arguments" : { "type" : <string: "all", or "uncaught">,
433 // "enabled" : <optional bool: enables the break type if true>
434 // }
435 // }
436 VARIANTMAPINIT;
437 jsonVal.insert(key: QLatin1String(COMMAND), value: QLatin1String(SETEXCEPTIONBREAK));
438
439 QJsonObject args;
440
441 if (type == All)
442 args.insert(key: QLatin1String(TYPE), value: QLatin1String(ALL));
443 else if (type == Uncaught)
444 args.insert(key: QLatin1String(TYPE), value: QLatin1String(UNCAUGHT));
445
446 if (enabled)
447 args.insert(key: QLatin1String(ENABLED), value: enabled);
448
449 jsonVal.insert(key: QLatin1String(ARGUMENTS), value: args);
450 d->sendMessage(command: V8REQUEST, args: jsonVal);
451}
452
453void QV4DebugClient::version()
454{
455 // { "seq" : <number>,
456 // "type" : "request",
457 // "command" : "version",
458 // }
459 VARIANTMAPINIT;
460 jsonVal.insert(key: QLatin1String(COMMAND), value: QLatin1String(VERSION));
461 d->sendMessage(command: V8REQUEST, args: jsonVal);
462}
463
464QV4DebugClient::Response QV4DebugClient::response() const
465{
466 Q_D(const QV4DebugClient);
467 const QJsonObject value = QJsonDocument::fromJson(json: d->response).object();
468 return {
469 .command: value.value(key: QLatin1String(COMMAND)).toString(),
470 .body: value.value(key: QLatin1String("body"))
471 };
472}
473
474void QV4DebugClient::disconnect()
475{
476 // { "seq" : <number>,
477 // "type" : "request",
478 // "command" : "disconnect",
479 // }
480 VARIANTMAPINIT;
481 jsonVal.insert(key: QLatin1String(COMMAND), value: QLatin1String(DISCONNECT));
482 d->sendMessage(command: DISCONNECT, args: jsonVal);
483}
484
485void QV4DebugClientPrivate::onStateChanged(QQmlDebugClient::State state)
486{
487 if (state == QQmlDebugClient::Enabled)
488 flushSendBuffer();
489}
490
491void QV4DebugClient::messageReceived(const QByteArray &data)
492{
493 Q_D(QV4DebugClient);
494 QPacket ds(connection()->currentDataStreamVersion(), data);
495 QByteArray command;
496 ds >> command;
497
498 if (command == "V8DEBUG") {
499 QByteArray type;
500 ds >> type >> d->response;
501
502 if (type == CONNECT) {
503 emit connected();
504
505 } else if (type == INTERRUPT) {
506 emit interrupted();
507
508 } else if (type == V8MESSAGE) {
509 const QJsonObject value = QJsonDocument::fromJson(json: d->response).object();
510 QString type = value.value(key: QLatin1String(TYPE)).toString();
511
512 if (type == QLatin1String("response")) {
513
514 if (!value.value(key: QLatin1String("success")).toBool()) {
515 emit failure();
516 qDebug() << "Received success == false response from application:"
517 << value.value(key: QLatin1String("message")).toString();
518 return;
519 }
520
521 QString debugCommand(value.value(key: QLatin1String(COMMAND)).toString());
522 if (debugCommand == QLatin1String(BACKTRACE) ||
523 debugCommand == QLatin1String(LOOKUP) ||
524 debugCommand == QLatin1String(SETBREAKPOINT) ||
525 debugCommand == QLatin1String(EVALUATE) ||
526 debugCommand == QLatin1String(VERSION) ||
527 debugCommand == QLatin1String(DISCONNECT) ||
528 debugCommand == QLatin1String(GARBAGECOLLECTOR) ||
529 debugCommand == QLatin1String(CHANGEBREAKPOINT) ||
530 debugCommand == QLatin1String(CLEARBREAKPOINT) ||
531 debugCommand == QLatin1String(FRAME) ||
532 debugCommand == QLatin1String(SCOPE) ||
533 debugCommand == QLatin1String(SCOPES) ||
534 debugCommand == QLatin1String(SCRIPTS) ||
535 debugCommand == QLatin1String(SOURCE) ||
536 debugCommand == QLatin1String(SETEXCEPTIONBREAK)) {
537 emit result();
538 } else {
539 // DO NOTHING
540 }
541
542 } else if (type == QLatin1String(EVENT)) {
543 QString event(value.value(key: QLatin1String(EVENT)).toString());
544
545 if (event == QLatin1String("break") || event == QLatin1String("exception"))
546 emit stopped();
547 }
548 }
549 }
550}
551
552void QV4DebugClientPrivate::sendMessage(const QByteArray &command, const QJsonObject &args)
553{
554 Q_Q(QV4DebugClient);
555 const QByteArray msg = packMessage(type: command, object: args);
556 if (q->state() == QQmlDebugClient::Enabled) {
557 q->sendMessage(message: msg);
558 } else {
559 sendBuffer.append(t: msg);
560 }
561}
562
563void QV4DebugClientPrivate::flushSendBuffer()
564{
565 foreach (const QByteArray &msg, sendBuffer)
566 sendMessage(command: msg);
567 sendBuffer.clear();
568}
569
570QByteArray QV4DebugClientPrivate::packMessage(const QByteArray &type, const QJsonObject &object)
571{
572 QPacket rs(connection->currentDataStreamVersion());
573 QByteArray cmd = "V8DEBUG";
574 rs << cmd << type << QJsonDocument(object).toJson(format: QJsonDocument::Compact);
575 return rs.data();
576}
577
578QT_END_NAMESPACE
579

source code of qtdeclarative/src/qmldebug/qv4debugclient.cpp