From 33bfa05c59a0d4ebcc8943ff710ee1da207da014 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 16 Oct 2018 14:06:49 +0200 Subject: QmlDebug: Add V4 debug client from tst_qqmldebugjs.cpp This is useful for implementing additional V4 debug clients. Along the way, we also drop the dependency on QML by using the JSON API from QtCore for JSON manipulation. Task-number: QTBUG-66504 Change-Id: Ib51e8e97b030a49fe8185ad5354d1cca63efef4a Reviewed-by: Simon Hausmann --- src/qmldebug/qmldebug.pro | 7 +- src/qmldebug/qv4debugclient.cpp | 578 +++++++++++++ src/qmldebug/qv4debugclient_p.h | 121 +++ src/qmldebug/qv4debugclient_p_p.h | 80 ++ .../auto/qml/debugger/qqmldebugjs/qqmldebugjs.pro | 2 +- .../qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp | 898 +++------------------ 6 files changed, 900 insertions(+), 786 deletions(-) create mode 100644 src/qmldebug/qv4debugclient.cpp create mode 100644 src/qmldebug/qv4debugclient_p.h create mode 100644 src/qmldebug/qv4debugclient_p_p.h diff --git a/src/qmldebug/qmldebug.pro b/src/qmldebug/qmldebug.pro index 0807482d23..42b73e5711 100644 --- a/src/qmldebug/qmldebug.pro +++ b/src/qmldebug/qmldebug.pro @@ -14,7 +14,8 @@ SOURCES += \ qqmlprofilerevent.cpp \ qqmlprofilereventlocation.cpp \ qqmlprofilereventtype.cpp \ - qqmlprofilertypedevent.cpp + qqmlprofilertypedevent.cpp \ + qv4debugclient.cpp HEADERS += \ qqmldebugclient_p.h \ @@ -32,4 +33,6 @@ HEADERS += \ qqmlprofilereventreceiver_p.h \ qqmlprofilereventtype_p.h \ qqmlprofilertypedevent_p.h \ - qqmlprofilerclientdefinitions_p.h + qqmlprofilerclientdefinitions_p.h \ + qv4debugclient_p.h \ + qv4debugclient_p_p.h diff --git a/src/qmldebug/qv4debugclient.cpp b/src/qmldebug/qv4debugclient.cpp new file mode 100644 index 0000000000..76c2f1ebea --- /dev/null +++ b/src/qmldebug/qv4debugclient.cpp @@ -0,0 +1,578 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4debugclient_p.h" +#include "qv4debugclient_p_p.h" +#include "qqmldebugconnection_p.h" + +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +const char *V8REQUEST = "v8request"; +const char *V8MESSAGE = "v8message"; +const char *SEQ = "seq"; +const char *TYPE = "type"; +const char *COMMAND = "command"; +const char *ARGUMENTS = "arguments"; +const char *STEPACTION = "stepaction"; +const char *STEPCOUNT = "stepcount"; +const char *EXPRESSION = "expression"; +const char *FRAME = "frame"; +const char *CONTEXT = "context"; +const char *GLOBAL = "global"; +const char *DISABLEBREAK = "disable_break"; +const char *HANDLES = "handles"; +const char *INCLUDESOURCE = "includeSource"; +const char *FROMFRAME = "fromFrame"; +const char *TOFRAME = "toFrame"; +const char *BOTTOM = "bottom"; +const char *NUMBER = "number"; +const char *FRAMENUMBER = "frameNumber"; +const char *TYPES = "types"; +const char *IDS = "ids"; +const char *FILTER = "filter"; +const char *FROMLINE = "fromLine"; +const char *TOLINE = "toLine"; +const char *TARGET = "target"; +const char *LINE = "line"; +const char *COLUMN = "column"; +const char *ENABLED = "enabled"; +const char *CONDITION = "condition"; +const char *IGNORECOUNT = "ignoreCount"; +const char *BREAKPOINT = "breakpoint"; +const char *FLAGS = "flags"; + +const char *CONTINEDEBUGGING = "continue"; +const char *EVALUATE = "evaluate"; +const char *LOOKUP = "lookup"; +const char *BACKTRACE = "backtrace"; +const char *SCOPE = "scope"; +const char *SCOPES = "scopes"; +const char *SCRIPTS = "scripts"; +const char *SOURCE = "source"; +const char *SETBREAKPOINT = "setbreakpoint"; +const char *CLEARBREAKPOINT = "clearbreakpoint"; +const char *CHANGEBREAKPOINT = "changebreakpoint"; +const char *SETEXCEPTIONBREAK = "setexceptionbreak"; +const char *VERSION = "version"; +const char *DISCONNECT = "disconnect"; +const char *GARBAGECOLLECTOR = "gc"; + +const char *CONNECT = "connect"; +const char *INTERRUPT = "interrupt"; + +const char *REQUEST = "request"; +const char *IN = "in"; +const char *NEXT = "next"; +const char *OUT = "out"; + +const char *SCRIPT = "script"; +const char *SCRIPTREGEXP = "scriptRegExp"; +const char *EVENT = "event"; + +const char *ALL = "all"; +const char *UNCAUGHT = "uncaught"; + +#define VARIANTMAPINIT \ + Q_D(QV4DebugClient); \ + QJsonObject jsonVal; \ + jsonVal.insert(QLatin1String(SEQ), d->seq++); \ + jsonVal.insert(QLatin1String(TYPE), QLatin1String(REQUEST)); + +QV4DebugClient::QV4DebugClient(QQmlDebugConnection *connection) + : QQmlDebugClient(*new QV4DebugClientPrivate(connection)) +{ + QObject::connect(this, &QQmlDebugClient::stateChanged, + this, [this](State state) { d_func()->onStateChanged(state); }); +} + +QV4DebugClientPrivate::QV4DebugClientPrivate(QQmlDebugConnection *connection) : + QQmlDebugClientPrivate(QLatin1String("V8Debugger"), connection) +{ +} + +void QV4DebugClient::connect() +{ + Q_D(QV4DebugClient); + d->sendMessage(CONNECT); +} + +void QV4DebugClient::interrupt() +{ + Q_D(QV4DebugClient); + d->sendMessage(INTERRUPT); +} + +void QV4DebugClient::continueDebugging(StepAction action) +{ + // { "seq" : , + // "type" : "request", + // "command" : "continue", + // "arguments" : { "stepaction" : <"in", "next" or "out">, + // "stepcount" : + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(CONTINEDEBUGGING)); + + if (action != Continue) { + QJsonObject args; + switch (action) { + case In: + args.insert(QLatin1String(STEPACTION), QLatin1String(IN)); + break; + case Out: + args.insert(QLatin1String(STEPACTION), QLatin1String(OUT)); + break; + case Next: + args.insert(QLatin1String(STEPACTION), QLatin1String(NEXT)); + break; + default: + break; + } + jsonVal.insert(QLatin1String(ARGUMENTS), args); + } + + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::evaluate(const QString &expr, int frame, int context) +{ + // { "seq" : , + // "type" : "request", + // "command" : "evaluate", + // "arguments" : { "expression" : , + // "frame" : , + // "context" : + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(EVALUATE)); + + QJsonObject args; + args.insert(QLatin1String(EXPRESSION), expr); + + if (frame != -1) + args.insert(QLatin1String(FRAME), frame); + + if (context != -1) + args.insert(QLatin1String(CONTEXT), context); + + jsonVal.insert(QLatin1String(ARGUMENTS), args); + + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::lookup(const QList &handles, bool includeSource) +{ + // { "seq" : , + // "type" : "request", + // "command" : "lookup", + // "arguments" : { "handles" : , + // "includeSource" : , + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND),(QLatin1String(LOOKUP))); + + QJsonObject args; + QJsonArray array; + + for (int handle : handles) + array.append(handle); + + args.insert(QLatin1String(HANDLES), array); + + if (includeSource) + args.insert(QLatin1String(INCLUDESOURCE), includeSource); + + jsonVal.insert(QLatin1String(ARGUMENTS), args); + + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::backtrace(int fromFrame, int toFrame, bool bottom) +{ + // { "seq" : , + // "type" : "request", + // "command" : "backtrace", + // "arguments" : { "fromFrame" : + // "toFrame" : + // "bottom" : + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(BACKTRACE)); + + QJsonObject args; + + if (fromFrame != -1) + args.insert(QLatin1String(FROMFRAME), fromFrame); + + if (toFrame != -1) + args.insert(QLatin1String(TOFRAME), toFrame); + + if (bottom) + args.insert(QLatin1String(BOTTOM), bottom); + + jsonVal.insert(QLatin1String(ARGUMENTS), args); + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::frame(int number) +{ + // { "seq" : , + // "type" : "request", + // "command" : "frame", + // "arguments" : { "number" : + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(FRAME)); + + if (number != -1) { + QJsonObject args; + args.insert(QLatin1String(NUMBER), number); + jsonVal.insert(QLatin1String(ARGUMENTS), args); + } + + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::scope(int number, int frameNumber) +{ + // { "seq" : , + // "type" : "request", + // "command" : "scope", + // "arguments" : { "number" : + // "frameNumber" : + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(SCOPE)); + + if (number != -1) { + QJsonObject args; + args.insert(QLatin1String(NUMBER), number); + + if (frameNumber != -1) + args.insert(QLatin1String(FRAMENUMBER), frameNumber); + + jsonVal.insert(QLatin1String(ARGUMENTS), args); + } + + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::scripts(int types, const QList &ids, bool includeSource) +{ + // { "seq" : , + // "type" : "request", + // "command" : "scripts", + // "arguments" : { "types" : + // "ids" : + // "includeSource" : + // "filter" : + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(SCRIPTS)); + + QJsonObject args; + args.insert(QLatin1String(TYPES), types); + + if (ids.count()) { + QJsonArray array; + for (int id : ids) + array.append(id); + + args.insert(QLatin1String(IDS), array); + } + + if (includeSource) + args.insert(QLatin1String(INCLUDESOURCE), includeSource); + + jsonVal.insert(QLatin1String(ARGUMENTS), args); + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::setBreakpoint(const QString &target, int line, int column, bool enabled, + const QString &condition, int ignoreCount) +{ + // { "seq" : , + // "type" : "request", + // "command" : "setbreakpoint", + // "arguments" : { "type" : "scriptRegExp" + // "target" : + // "line" : + // "column" : + // "enabled" : + // "condition" : + // "ignoreCount" : + // } + // } + + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(SETBREAKPOINT)); + + QJsonObject args; + + args.insert(QLatin1String(TYPE), QLatin1String(SCRIPTREGEXP)); + args.insert(QLatin1String(TARGET), target); + + if (line != -1) + args.insert(QLatin1String(LINE), line); + + if (column != -1) + args.insert(QLatin1String(COLUMN), column); + + args.insert(QLatin1String(ENABLED), enabled); + + if (!condition.isEmpty()) + args.insert(QLatin1String(CONDITION), condition); + + if (ignoreCount != -1) + args.insert(QLatin1String(IGNORECOUNT), ignoreCount); + + jsonVal.insert(QLatin1String(ARGUMENTS),args); + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::clearBreakpoint(int breakpoint) +{ + // { "seq" : , + // "type" : "request", + // "command" : "clearbreakpoint", + // "arguments" : { "breakpoint" : + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(CLEARBREAKPOINT)); + + QJsonObject args; + args.insert(QLatin1String(BREAKPOINT), breakpoint); + jsonVal.insert(QLatin1String(ARGUMENTS),args); + + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::changeBreakpoint(int breakpoint, bool enabled) +{ + // { "seq" : , + // "type" : "request", + // "command" : "changebreakpoint", + // "arguments" : { "breakpoint" : + // "enabled" : + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(CHANGEBREAKPOINT)); + + QJsonObject args; + args.insert(QLatin1String(BREAKPOINT), breakpoint); + args.insert(QLatin1String(ENABLED), enabled); + + jsonVal.insert(QLatin1String(ARGUMENTS), args); + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::setExceptionBreak(Exception type, bool enabled) +{ + // { "seq" : , + // "type" : "request", + // "command" : "setexceptionbreak", + // "arguments" : { "type" : , + // "enabled" : + // } + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(SETEXCEPTIONBREAK)); + + QJsonObject args; + + if (type == All) + args.insert(QLatin1String(TYPE), QLatin1String(ALL)); + else if (type == Uncaught) + args.insert(QLatin1String(TYPE), QLatin1String(UNCAUGHT)); + + if (enabled) + args.insert(QLatin1String(ENABLED), enabled); + + jsonVal.insert(QLatin1String(ARGUMENTS), args); + d->sendMessage(V8REQUEST, jsonVal); +} + +void QV4DebugClient::version() +{ + // { "seq" : , + // "type" : "request", + // "command" : "version", + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(VERSION)); + d->sendMessage(V8REQUEST, jsonVal); +} + +QV4DebugClient::Response QV4DebugClient::response() const +{ + Q_D(const QV4DebugClient); + const QJsonObject value = QJsonDocument::fromJson(d->response).object(); + return { + value.value(QLatin1String(COMMAND)).toString(), + value.value(QLatin1String("body")) + }; +} + +void QV4DebugClient::disconnect() +{ + // { "seq" : , + // "type" : "request", + // "command" : "disconnect", + // } + VARIANTMAPINIT; + jsonVal.insert(QLatin1String(COMMAND), QLatin1String(DISCONNECT)); + d->sendMessage(DISCONNECT, jsonVal); +} + +void QV4DebugClientPrivate::onStateChanged(QQmlDebugClient::State state) +{ + if (state == QQmlDebugClient::Enabled) + flushSendBuffer(); +} + +void QV4DebugClient::messageReceived(const QByteArray &data) +{ + Q_D(QV4DebugClient); + QPacket ds(connection()->currentDataStreamVersion(), data); + QByteArray command; + ds >> command; + + if (command == "V8DEBUG") { + QByteArray type; + ds >> type >> d->response; + + if (type == CONNECT) { + emit connected(); + + } else if (type == INTERRUPT) { + emit interrupted(); + + } else if (type == V8MESSAGE) { + const QJsonObject value = QJsonDocument::fromJson(d->response).object(); + QString type = value.value(QLatin1String(TYPE)).toString(); + + if (type == QLatin1String("response")) { + + if (!value.value(QLatin1String("success")).toBool()) { + emit failure(); + qDebug() << "Received success == false response from application:" + << value.value(QLatin1String("message")).toString(); + return; + } + + QString debugCommand(value.value(QLatin1String(COMMAND)).toString()); + if (debugCommand == QLatin1String(BACKTRACE) || + debugCommand == QLatin1String(LOOKUP) || + debugCommand == QLatin1String(SETBREAKPOINT) || + debugCommand == QLatin1String(EVALUATE) || + debugCommand == QLatin1String(VERSION) || + debugCommand == QLatin1String(DISCONNECT) || + debugCommand == QLatin1String(GARBAGECOLLECTOR) || + debugCommand == QLatin1String(CHANGEBREAKPOINT) || + debugCommand == QLatin1String(CLEARBREAKPOINT) || + debugCommand == QLatin1String(FRAME) || + debugCommand == QLatin1String(SCOPE) || + debugCommand == QLatin1String(SCOPES) || + debugCommand == QLatin1String(SCRIPTS) || + debugCommand == QLatin1String(SOURCE) || + debugCommand == QLatin1String(SETEXCEPTIONBREAK)) { + emit result(); + } else { + // DO NOTHING + } + + } else if (type == QLatin1String(EVENT)) { + QString event(value.value(QLatin1String(EVENT)).toString()); + + if (event == QLatin1String("break") || event == QLatin1String("exception")) + emit stopped(); + } + } + } +} + +void QV4DebugClientPrivate::sendMessage(const QByteArray &command, const QJsonObject &args) +{ + Q_Q(QV4DebugClient); + const QByteArray msg = packMessage(command, args); + if (q->state() == QQmlDebugClient::Enabled) { + q->sendMessage(msg); + } else { + sendBuffer.append(msg); + } +} + +void QV4DebugClientPrivate::flushSendBuffer() +{ + foreach (const QByteArray &msg, sendBuffer) + sendMessage(msg); + sendBuffer.clear(); +} + +QByteArray QV4DebugClientPrivate::packMessage(const QByteArray &type, const QJsonObject &object) +{ + QPacket rs(connection->currentDataStreamVersion()); + QByteArray cmd = "V8DEBUG"; + rs << cmd << type << QJsonDocument(object).toJson(QJsonDocument::Compact); + return rs.data(); +} + +QT_END_NAMESPACE diff --git a/src/qmldebug/qv4debugclient_p.h b/src/qmldebug/qv4debugclient_p.h new file mode 100644 index 0000000000..fdcf4284c5 --- /dev/null +++ b/src/qmldebug/qv4debugclient_p.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4DEBUGCLIENT_P_H +#define QV4DEBUGCLIENT_P_H + +#include +#include + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QV4DebugClientPrivate; +class QV4DebugClient : public QQmlDebugClient +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QV4DebugClient) + +public: + enum StepAction + { + Continue, + In, + Out, + Next + }; + + enum Exception + { + All, + Uncaught + }; + + struct Response + { + QString command; + QJsonValue body; + }; + + QV4DebugClient(QQmlDebugConnection *connection); + + void connect(); + void disconnect(); + + void interrupt(); + void continueDebugging(StepAction stepAction); + void evaluate(const QString &expr, int frame = -1, int context = -1); + void lookup(const QList &handles, bool includeSource = false); + void backtrace(int fromFrame = -1, int toFrame = -1, bool bottom = false); + void frame(int number = -1); + void scope(int number = -1, int frameNumber = -1); + void scripts(int types = 4, const QList &ids = QList(), bool includeSource = false); + void setBreakpoint(const QString &target, int line = -1, int column = -1, bool enabled = true, + const QString &condition = QString(), int ignoreCount = -1); + void clearBreakpoint(int breakpoint); + void changeBreakpoint(int breakpoint, bool enabled); + void setExceptionBreak(Exception type, bool enabled = false); + void version(); + + Response response() const; + +protected: + void messageReceived(const QByteArray &data) override; + +signals: + void connected(); + void interrupted(); + void result(); + void failure(); + void stopped(); +}; + +QT_END_NAMESPACE + +#endif // QV4DEBUGCLIENT_P_H diff --git a/src/qmldebug/qv4debugclient_p_p.h b/src/qmldebug/qv4debugclient_p_p.h new file mode 100644 index 0000000000..993c281632 --- /dev/null +++ b/src/qmldebug/qv4debugclient_p_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4DEBUGCLIENT_P_P_H +#define QV4DEBUGCLIENT_P_P_H + +#include "qv4debugclient_p.h" +#include "qqmldebugclient_p_p.h" + +#include + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QV4DebugClientPrivate : public QQmlDebugClientPrivate +{ + Q_DECLARE_PUBLIC(QV4DebugClient) + +public: + QV4DebugClientPrivate(QQmlDebugConnection *connection); + + void sendMessage(const QByteArray &command, const QJsonObject &args = QJsonObject()); + void flushSendBuffer(); + QByteArray packMessage(const QByteArray &type, const QJsonObject &object); + void onStateChanged(QQmlDebugClient::State state); + + int seq = 0; + QList sendBuffer; + QByteArray response; +}; + +QT_END_NAMESPACE + +#endif // QV4DEBUGCLIENT_P_P_H diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs.pro b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs.pro index 8fef435d98..2a32fa445b 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs.pro +++ b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs.pro @@ -1,6 +1,6 @@ CONFIG += testcase TARGET = tst_qqmldebugjs -QT += qml testlib gui-private core-private +QT += testlib gui-private core-private macos:CONFIG -= app_bundle SOURCES += tst_qqmldebugjs.cpp diff --git a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp index 941303d3ef..79a1e56a41 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp +++ b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp @@ -31,7 +31,7 @@ #include "../shared/qqmlenginedebugclient.h" #include "../../../shared/util.h" -#include +#include #include #include @@ -43,72 +43,8 @@ #include #include #include -#include - -const char *V8REQUEST = "v8request"; -const char *V8MESSAGE = "v8message"; -const char *SEQ = "seq"; -const char *TYPE = "type"; -const char *COMMAND = "command"; -const char *ARGUMENTS = "arguments"; -const char *STEPACTION = "stepaction"; -const char *STEPCOUNT = "stepcount"; -const char *EXPRESSION = "expression"; -const char *FRAME = "frame"; -const char *CONTEXT = "context"; -const char *GLOBAL = "global"; -const char *DISABLEBREAK = "disable_break"; -const char *HANDLES = "handles"; -const char *INCLUDESOURCE = "includeSource"; -const char *FROMFRAME = "fromFrame"; -const char *TOFRAME = "toFrame"; -const char *BOTTOM = "bottom"; -const char *NUMBER = "number"; -const char *FRAMENUMBER = "frameNumber"; -const char *TYPES = "types"; -const char *IDS = "ids"; -const char *FILTER = "filter"; -const char *FROMLINE = "fromLine"; -const char *TOLINE = "toLine"; -const char *TARGET = "target"; -const char *LINE = "line"; -const char *COLUMN = "column"; -const char *ENABLED = "enabled"; -const char *CONDITION = "condition"; -const char *IGNORECOUNT = "ignoreCount"; -const char *BREAKPOINT = "breakpoint"; -const char *FLAGS = "flags"; - -const char *CONTINEDEBUGGING = "continue"; -const char *EVALUATE = "evaluate"; -const char *LOOKUP = "lookup"; -const char *BACKTRACE = "backtrace"; -const char *SCOPE = "scope"; -const char *SCOPES = "scopes"; -const char *SCRIPTS = "scripts"; -const char *SOURCE = "source"; -const char *SETBREAKPOINT = "setbreakpoint"; -const char *CLEARBREAKPOINT = "clearbreakpoint"; -const char *CHANGEBREAKPOINT = "changebreakpoint"; -const char *SETEXCEPTIONBREAK = "setexceptionbreak"; -const char *VERSION = "version"; -const char *DISCONNECT = "disconnect"; -const char *GARBAGECOLLECTOR = "gc"; - -const char *CONNECT = "connect"; -const char *INTERRUPT = "interrupt"; - -const char *REQUEST = "request"; -const char *IN = "in"; -const char *NEXT = "next"; -const char *OUT = "out"; - -const char *SCRIPT = "script"; -const char *SCRIPTREGEXP = "scriptRegExp"; -const char *EVENT = "event"; - -const char *ALL = "all"; -const char *UNCAUGHT = "uncaught"; +#include +#include const char *BLOCKMODE = "-qmljsdebugger=port:3771,3800,block"; const char *NORMALMODE = "-qmljsdebugger=port:3771,3800"; @@ -129,13 +65,6 @@ const char *BREAKPOINTRELOCATION_QMLFILE = "breakpointRelocation.qml"; const char *ENCODEQMLSCOPE_QMLFILE = "encodeQmlScope.qml"; const char *BREAKONANCHOR_QMLFILE = "breakOnAnchor.qml"; -#define VARIANTMAPINIT \ - QString obj("{}"); \ - QJSValue jsonVal = parser.call(QJSValueList() << obj); \ - jsonVal.setProperty(SEQ,QJSValue(seq++)); \ - jsonVal.setProperty(TYPE,REQUEST); - - #undef QVERIFY #define QVERIFY(statement) \ do {\ @@ -146,9 +75,6 @@ do {\ }\ } while (0) - -class QJSDebugClient; - class tst_QQmlDebugJS : public QQmlDebugTest { Q_OBJECT @@ -231,7 +157,7 @@ private: ConnectResult init(bool qmlscene, const QString &qmlFile = QString(TEST_QMLFILE), bool blockMode = true, bool restrictServices = false); QList createClients() override; - QPointer m_client; + QPointer m_client; void targetData(); bool waitForClientSignal(const char *signal, int timeout = 30000); @@ -240,555 +166,7 @@ private: QTime t; }; -class QJSDebugClient : public QQmlDebugClient -{ - Q_OBJECT -public: - enum StepAction - { - Continue, - In, - Out, - Next - }; - - enum Exception - { - All, - Uncaught - }; - - QJSDebugClient(QQmlDebugConnection *connection) - : QQmlDebugClient(QLatin1String("V8Debugger"), connection), - seq(0) - { - parser = jsEngine.evaluate(QLatin1String("JSON.parse")); - stringify = jsEngine.evaluate(QLatin1String("JSON.stringify")); - QObject::connect(this, &QQmlDebugClient::stateChanged, - this, &QJSDebugClient::onStateChanged); - } - - void connect(); - void interrupt(); - - void continueDebugging(StepAction stepAction); - void evaluate(QString expr, int frame = -1, int context = -1); - void lookup(QList handles, bool includeSource = false); - void backtrace(int fromFrame = -1, int toFrame = -1, bool bottom = false); - void frame(int number = -1); - void scope(int number = -1, int frameNumber = -1); - void scripts(int types = 4, QList ids = QList(), bool includeSource = false, QVariant filter = QVariant()); - void setBreakpoint(QString target, int line = -1, int column = -1, bool enabled = true, - QString condition = QString(), int ignoreCount = -1); - void clearBreakpoint(int breakpoint); - void changeBreakpoint(int breakpoint, bool enabled); - void setExceptionBreak(Exception type, bool enabled = false); - void version(); - void disconnect(); - -protected: - //inherited from QQmlDebugClient - void onStateChanged(State state); - void messageReceived(const QByteArray &data); - -signals: - void connected(); - void interruptRequested(); - void result(); - void failure(); - void stopped(); - -private: - void sendMessage(const QByteArray &); - void flushSendBuffer(); - QByteArray packMessage(const QByteArray &type, const QByteArray &message = QByteArray()); - -private: - QJSEngine jsEngine; - int seq; - - QList sendBuffer; -public: - QJSValue parser; - QJSValue stringify; - QByteArray response; - -}; - -void QJSDebugClient::connect() -{ - const QJSValue jsonVal = parser.call(QJSValueList() << QLatin1String("{}")); - sendMessage(packMessage(CONNECT, - stringify.call(QJSValueList() << jsonVal).toString().toUtf8())); -} - -void QJSDebugClient::interrupt() -{ - sendMessage(packMessage(INTERRUPT)); -} - -void QJSDebugClient::continueDebugging(StepAction action) -{ - // { "seq" : , - // "type" : "request", - // "command" : "continue", - // "arguments" : { "stepaction" : <"in", "next" or "out">, - // "stepcount" : - // } - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(CONTINEDEBUGGING))); - - if (action != Continue) { - QJSValue args = parser.call(QJSValueList() << obj); - switch (action) { - case In: args.setProperty(QLatin1String(STEPACTION),QJSValue(QLatin1String(IN))); - break; - case Out: args.setProperty(QLatin1String(STEPACTION),QJSValue(QLatin1String(OUT))); - break; - case Next: args.setProperty(QLatin1String(STEPACTION),QJSValue(QLatin1String(NEXT))); - break; - default:break; - } - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - } - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::evaluate(QString expr, int frame, int context) -{ - // { "seq" : , - // "type" : "request", - // "command" : "evaluate", - // "arguments" : { "expression" : , - // "frame" : , - // "context" : - // } - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(EVALUATE))); - - QJSValue args = parser.call(QJSValueList() << obj); - args.setProperty(QLatin1String(EXPRESSION),QJSValue(expr)); - - if (frame != -1) - args.setProperty(QLatin1String(FRAME),QJSValue(frame)); - - if (context != -1) - args.setProperty(QLatin1String(CONTEXT), QJSValue(context)); - - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::lookup(QList handles, bool includeSource) -{ - // { "seq" : , - // "type" : "request", - // "command" : "lookup", - // "arguments" : { "handles" : , - // "includeSource" : , - // } - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(LOOKUP))); - - QJSValue args = parser.call(QJSValueList() << obj); - - QString arr("[]"); - QJSValue array = parser.call(QJSValueList() << arr); - int index = 0; - foreach (int handle, handles) { - array.setProperty(index++,QJSValue(handle)); - } - args.setProperty(QLatin1String(HANDLES),array); - - if (includeSource) - args.setProperty(QLatin1String(INCLUDESOURCE),QJSValue(includeSource)); - - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::backtrace(int fromFrame, int toFrame, bool bottom) -{ - // { "seq" : , - // "type" : "request", - // "command" : "backtrace", - // "arguments" : { "fromFrame" : - // "toFrame" : - // "bottom" : - // } - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(BACKTRACE))); - - QJSValue args = parser.call(QJSValueList() << obj); - - if (fromFrame != -1) - args.setProperty(QLatin1String(FROMFRAME),QJSValue(fromFrame)); - - if (toFrame != -1) - args.setProperty(QLatin1String(TOFRAME),QJSValue(toFrame)); - - if (bottom) - args.setProperty(QLatin1String(BOTTOM),QJSValue(bottom)); - - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::frame(int number) -{ - // { "seq" : , - // "type" : "request", - // "command" : "frame", - // "arguments" : { "number" : - // } - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(FRAME))); - - if (number != -1) { - QJSValue args = parser.call(QJSValueList() << obj); - args.setProperty(QLatin1String(NUMBER),QJSValue(number)); - - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - } - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::scope(int number, int frameNumber) -{ - // { "seq" : , - // "type" : "request", - // "command" : "scope", - // "arguments" : { "number" : - // "frameNumber" : - // } - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SCOPE))); - - if (number != -1) { - QJSValue args = parser.call(QJSValueList() << obj); - args.setProperty(QLatin1String(NUMBER),QJSValue(number)); - - if (frameNumber != -1) - args.setProperty(QLatin1String(FRAMENUMBER),QJSValue(frameNumber)); - - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - } - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::scripts(int types, QList ids, bool includeSource, QVariant /*filter*/) -{ - // { "seq" : , - // "type" : "request", - // "command" : "scripts", - // "arguments" : { "types" : - // "ids" : - // "includeSource" : - // "filter" : - // } - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SCRIPTS))); - - QJSValue args = parser.call(QJSValueList() << obj); - args.setProperty(QLatin1String(TYPES),QJSValue(types)); - - if (ids.count()) { - QString arr("[]"); - QJSValue array = parser.call(QJSValueList() << arr); - int index = 0; - foreach (int id, ids) { - array.setProperty(index++,QJSValue(id)); - } - args.setProperty(QLatin1String(IDS),array); - } - - if (includeSource) - args.setProperty(QLatin1String(INCLUDESOURCE),QJSValue(includeSource)); - - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::setBreakpoint(QString target, int line, int column, bool enabled, - QString condition, int ignoreCount) -{ - // { "seq" : , - // "type" : "request", - // "command" : "setbreakpoint", - // "arguments" : { "type" : "scriptRegExp" - // "target" : - // "line" : - // "column" : - // "enabled" : - // "condition" : - // "ignoreCount" : - // } - // } - - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SETBREAKPOINT))); - - QJSValue args = parser.call(QJSValueList() << obj); - args.setProperty(QLatin1String(TYPE),QJSValue(QLatin1String(SCRIPTREGEXP))); - args.setProperty(QLatin1String(TARGET),QJSValue(target)); - - if (line != -1) - args.setProperty(QLatin1String(LINE),QJSValue(line)); - - if (column != -1) - args.setProperty(QLatin1String(COLUMN),QJSValue(column)); - - args.setProperty(QLatin1String(ENABLED),QJSValue(enabled)); - - if (!condition.isEmpty()) - args.setProperty(QLatin1String(CONDITION),QJSValue(condition)); - - if (ignoreCount != -1) - args.setProperty(QLatin1String(IGNORECOUNT),QJSValue(ignoreCount)); - - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::clearBreakpoint(int breakpoint) -{ - // { "seq" : , - // "type" : "request", - // "command" : "clearbreakpoint", - // "arguments" : { "breakpoint" : - // } - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(CLEARBREAKPOINT))); - - QJSValue args = parser.call(QJSValueList() << obj); - - args.setProperty(QLatin1String(BREAKPOINT),QJSValue(breakpoint)); - - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::changeBreakpoint(int breakpoint, bool enabled) -{ - // { "seq" : , - // "type" : "request", - // "command" : "changebreakpoint", - // "arguments" : { "breakpoint" : - // "enabled" : - // } - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND), QLatin1String(CHANGEBREAKPOINT)); - - QJSValue args = parser.call(QJSValueList() << obj); - - args.setProperty(QLatin1String(BREAKPOINT), breakpoint); - args.setProperty(QLatin1String(ENABLED), enabled); - jsonVal.setProperty(QLatin1String(ARGUMENTS), args); - - const QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::setExceptionBreak(Exception type, bool enabled) -{ - // { "seq" : , - // "type" : "request", - // "command" : "setexceptionbreak", - // "arguments" : { "type" : , - // "enabled" : - // } - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SETEXCEPTIONBREAK))); - - QJSValue args = parser.call(QJSValueList() << obj); - - if (type == All) - args.setProperty(QLatin1String(TYPE),QJSValue(QLatin1String(ALL))); - else if (type == Uncaught) - args.setProperty(QLatin1String(TYPE),QJSValue(QLatin1String(UNCAUGHT))); - - if (enabled) - args.setProperty(QLatin1String(ENABLED),QJSValue(enabled)); - - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::version() -{ - // { "seq" : , - // "type" : "request", - // "command" : "version", - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(VERSION))); - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::disconnect() -{ - // { "seq" : , - // "type" : "request", - // "command" : "disconnect", - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(DISCONNECT))); - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(DISCONNECT, json.toString().toUtf8())); -} - -void QJSDebugClient::onStateChanged(State state) -{ - if (state == Enabled) - flushSendBuffer(); -} - -void QJSDebugClient::messageReceived(const QByteArray &data) -{ - QPacket ds(connection()->currentDataStreamVersion(), data); - QByteArray command; - ds >> command; - - if (command == "V8DEBUG") { - QByteArray type; - ds >> type >> response; - - if (type == CONNECT) { - emit connected(); - - } else if (type == INTERRUPT) { - emit interruptRequested(); - - } else if (type == V8MESSAGE) { - QString jsonString(response); - QVariantMap value = parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - QString type = value.value("type").toString(); - - if (type == "response") { - - if (!value.value("success").toBool()) { - emit failure(); - qDebug() << "Received success == false response from application:" - << value.value("message").toString(); - return; - } - - QString debugCommand(value.value("command").toString()); - if (debugCommand == "backtrace" || - debugCommand == "lookup" || - debugCommand == "setbreakpoint" || - debugCommand == "evaluate" || - debugCommand == "version" || - debugCommand == "disconnect" || - debugCommand == "gc" || - debugCommand == "changebreakpoint" || - debugCommand == "clearbreakpoint" || - debugCommand == "frame" || - debugCommand == "scope" || - debugCommand == "scopes" || - debugCommand == "scripts" || - debugCommand == "source" || - debugCommand == "setexceptionbreak" /*|| - debugCommand == "profile"*/) { - emit result(); - } else { - // DO NOTHING - } - - } else if (type == QLatin1String(EVENT)) { - QString event(value.value(QLatin1String(EVENT)).toString()); - - if (event == "break" || - event == "exception") - emit stopped(); - } - - } - } -} - -void QJSDebugClient::sendMessage(const QByteArray &msg) -{ - if (state() == Enabled) { - QQmlDebugClient::sendMessage(msg); - } else { - sendBuffer.append(msg); - } -} - -void QJSDebugClient::flushSendBuffer() -{ - foreach (const QByteArray &msg, sendBuffer) - QQmlDebugClient::sendMessage(msg); - sendBuffer.clear(); -} - -QByteArray QJSDebugClient::packMessage(const QByteArray &type, const QByteArray &message) -{ - QPacket rs(connection()->currentDataStreamVersion()); - QByteArray cmd = "V8DEBUG"; - rs << cmd << type << message; - return rs.data(); -} void tst_QQmlDebugJS::initTestCase() { @@ -842,7 +220,7 @@ void tst_QQmlDebugJS::interrupt() m_client->connect(); m_client->interrupt(); - QVERIFY(waitForClientSignal(SIGNAL(interruptRequested()))); + QVERIFY(waitForClientSignal(SIGNAL(interrupted()))); } void tst_QQmlDebugJS::getVersion() @@ -896,13 +274,11 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnCompleted() m_client->connect(); QVERIFY(waitForClientSignal(SIGNAL(stopped()))); - QString jsonString(m_client->response); - QVariantMap value = m_client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); + const QJsonObject body = m_client->response().body.toObject(); QCOMPARE(body.value("sourceLine").toInt(), sourceLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(ONCOMPLETED_QMLFILE)); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(ONCOMPLETED_QMLFILE)); } void tst_QQmlDebugJS::setBreakpointInScriptOnComponentCreated() @@ -917,13 +293,11 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnComponentCreated() m_client->connect(); QVERIFY(waitForClientSignal(SIGNAL(stopped()))); - QString jsonString(m_client->response); - QVariantMap value = m_client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); + const QJsonObject body = m_client->response().body.toObject(); QCOMPARE(body.value("sourceLine").toInt(), sourceLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(ONCOMPLETED_QMLFILE)); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(ONCOMPLETED_QMLFILE)); } void tst_QQmlDebugJS::setBreakpointInScriptOnTimerCallback() @@ -939,13 +313,11 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnTimerCallback() m_client->setBreakpoint(QLatin1String(TIMER_QMLFILE), sourceLine, -1, true); QVERIFY(waitForClientSignal(SIGNAL(stopped()))); - QString jsonString(m_client->response); - QVariantMap value = m_client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); + const QJsonObject body = m_client->response().body.toObject(); QCOMPARE(body.value("sourceLine").toInt(), sourceLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(TIMER_QMLFILE)); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(TIMER_QMLFILE)); } void tst_QQmlDebugJS::setBreakpointInScriptInDifferentFile() @@ -960,13 +332,11 @@ void tst_QQmlDebugJS::setBreakpointInScriptInDifferentFile() m_client->connect(); QVERIFY(waitForClientSignal(SIGNAL(stopped()))); - QString jsonString(m_client->response); - QVariantMap value = m_client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); + const QJsonObject body = m_client->response().body.toObject(); QCOMPARE(body.value("sourceLine").toInt(), sourceLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(TEST_JSFILE)); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(TEST_JSFILE)); } void tst_QQmlDebugJS::setBreakpointInScriptOnComment() @@ -983,13 +353,11 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnComment() QEXPECT_FAIL("", "Relocation of breakpoints is disabled right now", Abort); QVERIFY(waitForClientSignal(SIGNAL(stopped()), 1)); - QString jsonString(m_client->response); - QVariantMap value = m_client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); + const QJsonObject body = m_client->response().body.toObject(); QCOMPARE(body.value("sourceLine").toInt(), actualLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(BREAKPOINTRELOCATION_QMLFILE)); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(BREAKPOINTRELOCATION_QMLFILE)); } void tst_QQmlDebugJS::setBreakpointInScriptOnEmptyLine() @@ -1006,13 +374,11 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnEmptyLine() QEXPECT_FAIL("", "Relocation of breakpoints is disabled right now", Abort); QVERIFY(waitForClientSignal(SIGNAL(stopped()), 1)); - QString jsonString(m_client->response); - QVariantMap value = m_client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); + const QJsonObject body = m_client->response().body.toObject(); QCOMPARE(body.value("sourceLine").toInt(), actualLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(BREAKPOINTRELOCATION_QMLFILE)); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(BREAKPOINTRELOCATION_QMLFILE)); } void tst_QQmlDebugJS::setBreakpointInScriptOnOptimizedBinding() @@ -1027,13 +393,11 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnOptimizedBinding() m_client->connect(); QVERIFY(waitForClientSignal(SIGNAL(stopped()))); - QString jsonString(m_client->response); - QVariantMap value = m_client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); + const QJsonObject body = m_client->response().body.toObject(); QCOMPARE(body.value("sourceLine").toInt(), sourceLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(BREAKPOINTRELOCATION_QMLFILE)); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(BREAKPOINTRELOCATION_QMLFILE)); } void tst_QQmlDebugJS::setBreakpointInScriptWithCondition() @@ -1050,11 +414,8 @@ void tst_QQmlDebugJS::setBreakpointInScriptWithCondition() QVERIFY(waitForClientSignal(SIGNAL(stopped()))); //Get the frame index - QString jsonString = m_client->response; - QVariantMap value = m_client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - { - QVariantMap body = value.value("body").toMap(); + const QJsonObject body = m_client->response().body.toObject(); int frameIndex = body.value("index").toInt(); //Verify the value of 'result' @@ -1062,13 +423,10 @@ void tst_QQmlDebugJS::setBreakpointInScriptWithCondition() QVERIFY(waitForClientSignal(SIGNAL(result()))); } - jsonString = m_client->response; - QJSValue val = m_client->parser.call(QJSValueList() << QJSValue(jsonString)); - QVERIFY(val.isObject()); - QJSValue body = val.property(QStringLiteral("body")); - QVERIFY(body.isObject()); - val = body.property("value"); - QVERIFY(val.isNumber()); + const QJsonObject body = m_client->response().body.toObject(); + QVERIFY(!body.isEmpty()); + QJsonValue val = body.value("value"); + QVERIFY(val.isDouble()); const int a = val.toInt(); QVERIFY(a > out); @@ -1086,15 +444,12 @@ void tst_QQmlDebugJS::setBreakpointInScriptThatQuits() m_client->connect(); QVERIFY(waitForClientSignal(SIGNAL(stopped()))); - QString jsonString(m_client->response); - QVariantMap value = m_client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); + const QJsonObject body = m_client->response().body.toObject(); QCOMPARE(body.value("sourceLine").toInt(), sourceLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(QUIT_QMLFILE)); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), QLatin1String(QUIT_QMLFILE)); - m_client->continueDebugging(QJSDebugClient::Continue); + m_client->continueDebugging(QV4DebugClient::Continue); QVERIFY(m_process->waitForFinished()); QCOMPARE(m_process->exitStatus(), QProcess::NormalExit); } @@ -1134,34 +489,32 @@ void tst_QQmlDebugJS::clearBreakpoint() QVERIFY(waitForClientSignal(SIGNAL(stopped()))); - //Will hit 1st brakpoint, change this breakpoint enable = false - QString jsonString(m_client->response); - QVariantMap value = m_client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + { + //Will hit 1st brakpoint, change this breakpoint enable = false + const QJsonObject body = m_client->response().body.toObject(); + const QJsonArray breakpointsHit = body.value("breakpoints").toArray(); - QVariantMap body = value.value("body").toMap(); - QList breakpointsHit = body.value("breakpoints").toList(); + int breakpoint = breakpointsHit.at(0).toInt(); + m_client->clearBreakpoint(breakpoint); - int breakpoint = breakpointsHit.at(0).toInt(); - m_client->clearBreakpoint(breakpoint); + QVERIFY(waitForClientSignal(SIGNAL(result()))); - QVERIFY(waitForClientSignal(SIGNAL(result()))); + //Continue with debugging + m_client->continueDebugging(QV4DebugClient::Continue); + //Hit 2nd breakpoint + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); - //Continue with debugging - m_client->continueDebugging(QJSDebugClient::Continue); - //Hit 2nd breakpoint - QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + //Continue with debugging + m_client->continueDebugging(QV4DebugClient::Continue); + } - //Continue with debugging - m_client->continueDebugging(QJSDebugClient::Continue); //Should stop at 2nd breakpoint QVERIFY(waitForClientSignal(SIGNAL(stopped()))); - jsonString = m_client->response; - value = m_client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - body = value.value("body").toMap(); - - QCOMPARE(body.value("sourceLine").toInt(), sourceLine2); + { + const QJsonObject body = m_client->response().body.toObject(); + QCOMPARE(body.value("sourceLine").toInt(), sourceLine2); + } } void tst_QQmlDebugJS::changeBreakpoint() @@ -1174,23 +527,21 @@ void tst_QQmlDebugJS::changeBreakpoint() QCOMPARE(init(qmlscene, CHANGEBREAKPOINT_QMLFILE), ConnectSuccess); bool isStopped = false; - QObject::connect(m_client, &QJSDebugClient::stopped, this, [&]() { isStopped = true; }); + QObject::connect(m_client, &QV4DebugClient::stopped, this, [&]() { isStopped = true; }); auto continueDebugging = [&]() { - m_client->continueDebugging(QJSDebugClient::Continue); + m_client->continueDebugging(QV4DebugClient::Continue); isStopped = false; }; m_client->connect(); auto extractBody = [&]() { - const QVariantMap value = m_client->parser.call( - QJSValueList() << QJSValue(QString(m_client->response))).toVariant().toMap(); - return value.value("body").toMap(); + return m_client->response().body.toObject(); }; - auto extractBreakPointId = [&](const QVariantMap &body) { - const QList breakpointsHit = body.value("breakpoints").toList(); + auto extractBreakPointId = [&](const QJsonObject &body) { + const QJsonArray breakpointsHit = body.value("breakpoints").toArray(); if (breakpointsHit.size() != 1) return -1; return breakpointsHit[0].toInt(); @@ -1198,7 +549,7 @@ void tst_QQmlDebugJS::changeBreakpoint() auto setBreakPoint = [&](int sourceLine, bool enabled) { int id = -1; - auto connection = QObject::connect(m_client, &QJSDebugClient::result, [&]() { + auto connection = QObject::connect(m_client, &QV4DebugClient::result, [&]() { id = extractBody().value("breakpoint").toInt(); }); @@ -1221,7 +572,7 @@ void tst_QQmlDebugJS::changeBreakpoint() auto verifyBreakpoint = [&](int sourceLine, int breakpointId) { QTRY_VERIFY_WITH_TIMEOUT(isStopped, 30000); - const QVariantMap body = extractBody(); + const QJsonObject body = extractBody(); QCOMPARE(body.value("sourceLine").toInt(), sourceLine); QCOMPARE(extractBreakPointId(body), breakpointId); }; @@ -1261,7 +612,7 @@ void tst_QQmlDebugJS::setExceptionBreak() QFETCH(bool, qmlscene); QCOMPARE(init(qmlscene, EXCEPTION_QMLFILE), ConnectSuccess); - m_client->setExceptionBreak(QJSDebugClient::All,true); + m_client->setExceptionBreak(QV4DebugClient::All,true); m_client->connect(); QVERIFY(waitForClientSignal(SIGNAL(stopped()))); } @@ -1278,24 +629,19 @@ void tst_QQmlDebugJS::stepNext() m_client->connect(); QVERIFY(waitForClientSignal(SIGNAL(stopped()))); - m_client->continueDebugging(QJSDebugClient::Next); + m_client->continueDebugging(QV4DebugClient::Next); QVERIFY(waitForClientSignal(SIGNAL(stopped()))); - QString jsonString(m_client->response); - QVariantMap value = m_client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); + const QJsonObject body = m_client->response().body.toObject(); QCOMPARE(body.value("sourceLine").toInt(), sourceLine + 1); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE)); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(STEPACTION_QMLFILE)); } -static QVariantMap responseBody(QJSDebugClient *client) +static QJsonObject responseBody(QV4DebugClient *client) { - const QString jsonString(client->response); - const QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)) - .toVariant().toMap(); - return value.value("body").toMap(); + return client->response().body.toObject(); } void tst_QQmlDebugJS::stepIn() @@ -1312,12 +658,12 @@ void tst_QQmlDebugJS::stepIn() QVERIFY(waitForClientSignal(SIGNAL(stopped()))); QCOMPARE(responseBody(m_client).value("sourceLine").toInt(), sourceLine); - m_client->continueDebugging(QJSDebugClient::In); + m_client->continueDebugging(QV4DebugClient::In); QVERIFY(waitForClientSignal(SIGNAL(stopped()))); - const QVariantMap body = responseBody(m_client); + const QJsonObject body = responseBody(m_client); QCOMPARE(body.value("sourceLine").toInt(), actualLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE)); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE)); } void tst_QQmlDebugJS::stepOut() @@ -1334,12 +680,12 @@ void tst_QQmlDebugJS::stepOut() QVERIFY(waitForClientSignal(SIGNAL(stopped()))); QCOMPARE(responseBody(m_client).value("sourceLine").toInt(), sourceLine); - m_client->continueDebugging(QJSDebugClient::Out); + m_client->continueDebugging(QV4DebugClient::Out); QVERIFY(waitForClientSignal(SIGNAL(stopped()))); - const QVariantMap body = responseBody(m_client); + const QJsonObject body = responseBody(m_client); QCOMPARE(body.value("sourceLine").toInt(), actualLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE)); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE)); } void tst_QQmlDebugJS::continueDebugging() @@ -1356,16 +702,14 @@ void tst_QQmlDebugJS::continueDebugging() m_client->connect(); QVERIFY(waitForClientSignal(SIGNAL(stopped()))); - m_client->continueDebugging(QJSDebugClient::Continue); + m_client->continueDebugging(QV4DebugClient::Continue); QVERIFY(waitForClientSignal(SIGNAL(stopped()))); - QString jsonString(m_client->response); - QVariantMap value = m_client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); + const QJsonObject body = responseBody(m_client); QCOMPARE(body.value("sourceLine").toInt(), sourceLine2); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE)); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(STEPACTION_QMLFILE)); } void tst_QQmlDebugJS::backtrace() @@ -1449,32 +793,27 @@ void tst_QQmlDebugJS::evaluateInLocalScope() m_client->frame(); QVERIFY(waitForClientSignal(SIGNAL(result()))); - //Get the frame index - QString jsonString(m_client->response); - QVariantMap value = m_client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); - - int frameIndex = body.value("index").toInt(); - - m_client->evaluate(QLatin1String("root.a"), frameIndex); - QVERIFY(waitForClientSignal(SIGNAL(result()))); - - //Verify the value of 'timer.interval' - jsonString = m_client->response; - value = m_client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - body = value.value("body").toMap(); + { + //Get the frame index + const QJsonObject body = responseBody(m_client); + int frameIndex = body.value("index").toInt(); + m_client->evaluate(QLatin1String("root.a"), frameIndex); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + } - QCOMPARE(body.value("value").toInt(),10); + { + //Verify the value of 'timer.interval' + const QJsonObject body = responseBody(m_client); + QCOMPARE(body.value("value").toInt(),10); + } } void tst_QQmlDebugJS::evaluateInContext() { m_connection = new QQmlDebugConnection(); - m_process = new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) - + "/qmlscene", this); - m_client = new QJSDebugClient(m_connection); + m_process = new QQmlDebugProcess( + QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", this); + m_client = new QV4DebugClient(m_connection); QScopedPointer engineClient(new QQmlEngineDebugClient(m_connection)); m_process->start(QStringList() << QLatin1String(BLOCKMODE) << testFile(ONCOMPLETED_QMLFILE)); @@ -1530,14 +869,12 @@ void tst_QQmlDebugJS::getScripts() m_client->scripts(); QVERIFY(waitForClientSignal(SIGNAL(result()))); - QString jsonString(m_client->response); - QVariantMap value = m_client->parser.call(QJSValueList() - << QJSValue(jsonString)).toVariant().toMap(); - QList scripts = value.value("body").toList(); + const QJsonArray scripts = m_client->response().body.toArray(); QCOMPARE(scripts.count(), 1); - QVERIFY(scripts.first().toMap()[QStringLiteral("name")].toString().endsWith(QStringLiteral("data/test.qml"))); + QVERIFY(scripts.first().toObject()[QStringLiteral("name")].toString() + .endsWith(QStringLiteral("data/test.qml"))); } void tst_QQmlDebugJS::encodeQmlScope() @@ -1551,42 +888,38 @@ void tst_QQmlDebugJS::encodeQmlScope() bool isStopped = false; bool scopesFailed = false; - QObject::connect(m_client, &QJSDebugClient::failure, this, [&]() { - qWarning() << "received failure" << m_client->response; + QObject::connect(m_client, &QV4DebugClient::failure, this, [&]() { + qWarning() << "received failure" << m_client->response().body; scopesFailed = true; m_process->stop(); numFrames = 2; isStopped = false; }); - QObject::connect(m_client, &QJSDebugClient::stopped, this, [&]() { + QObject::connect(m_client, &QV4DebugClient::stopped, this, [&]() { m_client->frame(); isStopped = true; }); - QObject::connect(m_client, &QJSDebugClient::result, this, [&]() { - const QVariantMap value = m_client->parser.call( - QJSValueList() << QJSValue(QString(m_client->response))).toVariant().toMap(); - - const QMap body = value.value("body").toMap(); - const QString command = value.value("command").toString(); + QObject::connect(m_client, &QV4DebugClient::result, this, [&]() { + const QV4DebugClient::Response value = m_client->response(); - if (command == QString("scope")) { + if (value.command == QString("scope")) { // If the scope commands fail we get a failure() signal above. if (++numReceivedScopes == numExpectedScopes) { - m_client->continueDebugging(QJSDebugClient::Continue); + m_client->continueDebugging(QV4DebugClient::Continue); isStopped = false; } - } else if (command == QString("frame")) { + } else if (value.command == QString("frame")) { // We want at least a global scope and some kind of local scope here. - const QList scopes = body.value("scopes").toList(); - if (scopes.length() < 2) + const QJsonArray scopes = value.body.toObject().value("scopes").toArray(); + if (scopes.count() < 2) scopesFailed = true; - for (const QVariant &scope : scopes) { + for (const QJsonValue &scope : scopes) { ++numExpectedScopes; - m_client->scope(scope.toMap().value("index").toInt()); + m_client->scope(scope.toObject().value("index").toInt()); } ++numFrames; @@ -1611,21 +944,21 @@ void tst_QQmlDebugJS::breakOnAnchor() int breaks = 0; bool stopped = false; - QObject::connect(m_client, &QJSDebugClient::stopped, this, [&]() { + QObject::connect(m_client, &QV4DebugClient::stopped, this, [&]() { stopped = true; ++breaks; m_client->evaluate("this", 0, -1); }); - QObject::connect(m_client, &QJSDebugClient::result, this, [&]() { + QObject::connect(m_client, &QV4DebugClient::result, this, [&]() { if (stopped) { - m_client->continueDebugging(QJSDebugClient::Continue); + m_client->continueDebugging(QV4DebugClient::Continue); stopped = false; } }); - QObject::connect(m_client, &QJSDebugClient::failure, this, [&]() { - qWarning() << "received failure" << m_client->response; + QObject::connect(m_client, &QV4DebugClient::failure, this, [&]() { + qWarning() << "received failure" << m_client->response().body; }); m_client->setBreakpoint(file, 34); @@ -1643,7 +976,7 @@ void tst_QQmlDebugJS::breakOnAnchor() QList tst_QQmlDebugJS::createClients() { - m_client = new QJSDebugClient(m_connection); + m_client = new QV4DebugClient(m_connection); return QList({m_client}); } @@ -1661,10 +994,9 @@ bool tst_QQmlDebugJS::waitForClientSignal(const char *signal, int timeout) void tst_QQmlDebugJS::checkVersionParameters() { - const QVariantMap value = m_client->parser.call( - QJSValueList() << QJSValue(QString(m_client->response))).toVariant().toMap(); - QCOMPARE(value.value("command").toString(), QString("version")); - const QVariantMap body = value.value("body").toMap(); + const QV4DebugClient::Response value = m_client->response(); + QCOMPARE(value.command, QString("version")); + const QJsonObject body = value.body.toObject(); QCOMPARE(body.value("UnpausedEvaluate").toBool(), true); QCOMPARE(body.value("ContextEvaluate").toBool(), true); QCOMPARE(body.value("ChangeBreakpoint").toBool(), true); -- cgit v1.2.3