blob: 07db5234bf5ae35b89d06fa596306f740f8d43dc [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 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 "qv4debugservice.h"
#include "qv4debugjob.h"
#include "qqmlengine.h"
#include <private/qv4engine_p.h>
#include <private/qv4function_p.h>
#include <private/qqmldebugconnector_p.h>
#include <private/qversionedpacket_p.h>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonValue>
const char *const V4_CONNECT = "connect";
const char *const V4_DISCONNECT = "disconnect";
const char *const V4_BREAK_ON_SIGNAL = "breakonsignal";
const char *const V4_PAUSE = "interrupt";
#define NO_PROTOCOL_TRACING
#ifdef NO_PROTOCOL_TRACING
# define TRACE_PROTOCOL(x)
#else
#include <QtCore/QDebug>
# define TRACE_PROTOCOL(x) x
#endif
QT_BEGIN_NAMESPACE
class V4CommandHandler;
class UnknownV4CommandHandler;
using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>;
int QV4DebugServiceImpl::sequence = 0;
class V4CommandHandler
{
public:
V4CommandHandler(const QString &command)
: cmd(command)
{}
virtual ~V4CommandHandler()
{}
QString command() const { return cmd; }
void handle(const QJsonObject &request, QV4DebugServiceImpl *s)
{
TRACE_PROTOCOL(qDebug() << "handling command" << command() << "...");
req = request;
seq = req.value(QLatin1String("seq"));
debugService = s;
handleRequest();
if (!response.isEmpty()) {
response[QLatin1String("type")] = QStringLiteral("response");
debugService->send(response);
}
debugService = nullptr;
seq = QJsonValue();
req = QJsonObject();
response = QJsonObject();
}
virtual void handleRequest() = 0;
protected:
void addCommand() { response.insert(QStringLiteral("command"), cmd); }
void addRequestSequence() { response.insert(QStringLiteral("request_seq"), seq); }
void addSuccess(bool success) { response.insert(QStringLiteral("success"), success); }
void addBody(const QJsonValue &body)
{
response.insert(QStringLiteral("body"), body);
}
void addRunning()
{
response.insert(QStringLiteral("running"), debugService->debuggerAgent.isRunning());
}
void createErrorResponse(const QString &msg)
{
QJsonValue command = req.value(QLatin1String("command"));
response.insert(QStringLiteral("command"), command);
addRequestSequence();
addSuccess(false);
addRunning();
response.insert(QStringLiteral("message"), msg);
}
int requestSequenceNr() const
{ return seq.toInt(-1); }
protected:
QString cmd;
QJsonObject req;
QJsonValue seq;
QV4DebugServiceImpl *debugService;
QJsonObject response;
};
class UnknownV4CommandHandler: public V4CommandHandler
{
public:
UnknownV4CommandHandler(): V4CommandHandler(QString()) {}
void handleRequest() override
{
QString msg = QLatin1String("unimplemented command \"")
+ req.value(QLatin1String("command")).toString()
+ QLatin1Char('"');
createErrorResponse(msg);
}
};
namespace {
class V4VersionRequest: public V4CommandHandler
{
public:
V4VersionRequest(): V4CommandHandler(QStringLiteral("version")) {}
void handleRequest() override
{
addCommand();
addRequestSequence();
addSuccess(true);
addRunning();
QJsonObject body;
body.insert(QStringLiteral("V8Version"),
QLatin1String("this is not V8, this is V4 in Qt " QT_VERSION_STR));
body.insert(QStringLiteral("UnpausedEvaluate"), true);
body.insert(QStringLiteral("ContextEvaluate"), true);
body.insert(QStringLiteral("ChangeBreakpoint"), true);
addBody(body);
}
};
class V4BreakPointRequest: public V4CommandHandler
{
public:
V4BreakPointRequest(const QString &name): V4CommandHandler(name) {}
void handleRequest() final
{
// Other types are currently not supported
m_type = QStringLiteral("scriptRegExp");
// decypher the payload:
m_args = req.value(QLatin1String("arguments")).toObject();
if (m_args.isEmpty()) {
createErrorResponse(QStringLiteral("breakpoint request with empty arguments object"));
return;
}
const int id = handleBreakPointRequest();
if (id < 0) {
createErrorResponse(m_error);
} else {
// response:
addCommand();
addRequestSequence();
addSuccess(true);
addRunning();
QJsonObject body;
body.insert(QStringLiteral("type"), m_type);
body.insert(QStringLiteral("breakpoint"), id);
addBody(body);
}
}
protected:
virtual int handleBreakPointRequest() = 0;
QJsonObject m_args;
QString m_type;
QString m_error;
};
class V4SetBreakPointRequest: public V4BreakPointRequest
{
public:
V4SetBreakPointRequest(): V4BreakPointRequest(QStringLiteral("setbreakpoint")) {}
int handleBreakPointRequest() final
{
// decypher the payload:
const QString type = m_args.value(QLatin1String("type")).toString();
if (type != QLatin1String("scriptRegExp")) {
m_error = QStringLiteral("breakpoint type \"%1\" is not implemented").arg(type);
return -1;
}
const QString fileName = m_args.value(QLatin1String("target")).toString();
if (fileName.isEmpty()) {
m_error = QStringLiteral("breakpoint has no file name");
return -1;
}
const int line = m_args.value(QLatin1String("line")).toInt(-1);
if (line < 0) {
m_error = QStringLiteral("breakpoint has an invalid line number");
return -1;
}
const bool enabled = m_args.value(QStringLiteral("enabled")).toBool(true);
const QString condition = m_args.value(QStringLiteral("condition")).toString();
// set the break point:
return debugService->debuggerAgent.addBreakPoint(fileName, line + 1, enabled, condition);
// It's undocumented, but V8 sends back an actual_locations array too. However, our
// Debugger currently doesn't tell us when it resolved a breakpoint, so we'll leave them
// pending until the breakpoint is hit for the first time.
}
};
class V4ClearBreakPointRequest: public V4BreakPointRequest
{
public:
V4ClearBreakPointRequest(): V4BreakPointRequest(QStringLiteral("clearbreakpoint")) {}
int handleBreakPointRequest() final
{
const int id = m_args.value(QLatin1String("breakpoint")).toInt(-1);
if (id < 0)
m_error = QStringLiteral("breakpoint has an invalid number");
else // remove the break point:
debugService->debuggerAgent.removeBreakPoint(id);
return id;
}
};
class V4ChangeBreakPointRequest: public V4BreakPointRequest
{
public:
V4ChangeBreakPointRequest(): V4BreakPointRequest(QStringLiteral("changebreakpoint")) {}
int handleBreakPointRequest() final
{
const int id = m_args.value(QLatin1String("breakpoint")).toInt(-1);
if (id < 0) {
m_error = QStringLiteral("breakpoint has an invalid number");
return id;
}
const QJsonValue enabled = m_args.value(QLatin1String("enabled"));
if (!enabled.isBool()) {
m_error = QStringLiteral("missing bool \"enabled\" in breakpoint change request");
return -1;
}
// enable or disable the break point:
debugService->debuggerAgent.enableBreakPoint(id, enabled.toBool());
return id;
}
};
class V4BacktraceRequest: public V4CommandHandler
{
public:
V4BacktraceRequest(): V4CommandHandler(QStringLiteral("backtrace")) {}
void handleRequest() override
{
// decypher the payload:
QJsonObject arguments = req.value(QLatin1String("arguments")).toObject();
int fromFrame = arguments.value(QLatin1String("fromFrame")).toInt(0);
int toFrame = arguments.value(QLatin1String("toFrame")).toInt(fromFrame + 10);
// no idea what the bottom property is for, so we'll ignore it.
QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
if (!debugger) {
createErrorResponse(QStringLiteral("Debugger has to be paused to retrieve backtraces."));
return;
}
BacktraceJob job(debugger->collector(), fromFrame, toFrame);
debugger->runInEngine(&job);
// response:
addCommand();
addRequestSequence();
addSuccess(true);
addRunning();
addBody(job.returnValue());
}
};
class V4FrameRequest: public V4CommandHandler
{
public:
V4FrameRequest(): V4CommandHandler(QStringLiteral("frame")) {}
void handleRequest() override
{
// decypher the payload:
QJsonObject arguments = req.value(QLatin1String("arguments")).toObject();
const int frameNr = arguments.value(QLatin1String("number")).toInt(
debugService->selectedFrame());
QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
if (!debugger) {
createErrorResponse(QStringLiteral("Debugger has to be paused to retrieve frames."));
return;
}
if (frameNr < 0) {
createErrorResponse(QStringLiteral("frame command has invalid frame number"));
return;
}
FrameJob job(debugger->collector(), frameNr);
debugger->runInEngine(&job);
if (!job.wasSuccessful()) {
createErrorResponse(QStringLiteral("frame retrieval failed"));
return;
}
debugService->selectFrame(frameNr);
// response:
addCommand();
addRequestSequence();
addSuccess(true);
addRunning();
addBody(job.returnValue());
}
};
class V4ScopeRequest: public V4CommandHandler
{
public:
V4ScopeRequest(): V4CommandHandler(QStringLiteral("scope")) {}
void handleRequest() override
{
// decypher the payload:
QJsonObject arguments = req.value(QLatin1String("arguments")).toObject();
const int frameNr = arguments.value(QLatin1String("frameNumber")).toInt(
debugService->selectedFrame());
const int scopeNr = arguments.value(QLatin1String("number")).toInt(0);
QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
if (!debugger) {
createErrorResponse(QStringLiteral("Debugger has to be paused to retrieve scope."));
return;
}
if (frameNr < 0) {
createErrorResponse(QStringLiteral("scope command has invalid frame number"));
return;
}
if (scopeNr < 0) {
createErrorResponse(QStringLiteral("scope command has invalid scope number"));
return;
}
ScopeJob job(debugger->collector(), frameNr, scopeNr);
debugger->runInEngine(&job);
if (!job.wasSuccessful()) {
createErrorResponse(QStringLiteral("scope retrieval failed"));
return;
}
// response:
addCommand();
addRequestSequence();
addSuccess(true);
addRunning();
addBody(job.returnValue());
}
};
class V4LookupRequest: public V4CommandHandler
{
public:
V4LookupRequest(): V4CommandHandler(QStringLiteral("lookup")) {}
void handleRequest() override
{
// decypher the payload:
QJsonObject arguments = req.value(QLatin1String("arguments")).toObject();
QJsonArray handles = arguments.value(QLatin1String("handles")).toArray();
QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
if (!debugger) {
const QList<QV4Debugger *> &debuggers = debugService->debuggerAgent.debuggers();
if (debuggers.count() > 1) {
createErrorResponse(QStringLiteral("Cannot lookup values if multiple debuggers are running and none is paused"));
return;
} else if (debuggers.count() == 0) {
createErrorResponse(QStringLiteral("No debuggers available to lookup values"));
return;
}
debugger = debuggers.first();
}
ValueLookupJob job(handles, debugger->collector());
debugger->runInEngine(&job);
if (!job.exceptionMessage().isEmpty()) {
createErrorResponse(job.exceptionMessage());
} else {
// response:
addCommand();
addRequestSequence();
addSuccess(true);
addRunning();
addBody(job.returnValue());
}
}
};
class V4ContinueRequest: public V4CommandHandler
{
public:
V4ContinueRequest(): V4CommandHandler(QStringLiteral("continue")) {}
void handleRequest() override
{
// decypher the payload:
QJsonObject arguments = req.value(QLatin1String("arguments")).toObject();
QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
if (!debugger) {
createErrorResponse(QStringLiteral("Debugger has to be paused in order to continue."));
return;
}
debugService->debuggerAgent.clearAllPauseRequests();
if (arguments.empty()) {
debugger->resume(QV4Debugger::FullThrottle);
} else {
QJsonObject arguments = req.value(QLatin1String("arguments")).toObject();
QString stepAction = arguments.value(QLatin1String("stepaction")).toString();
const int stepcount = arguments.value(QLatin1String("stepcount")).toInt(1);
if (stepcount != 1)
qWarning() << "Step count other than 1 is not supported.";
if (stepAction == QLatin1String("in")) {
debugger->resume(QV4Debugger::StepIn);
} else if (stepAction == QLatin1String("out")) {
debugger->resume(QV4Debugger::StepOut);
} else if (stepAction == QLatin1String("next")) {
debugger->resume(QV4Debugger::StepOver);
} else {
createErrorResponse(QStringLiteral("continue command has invalid stepaction"));
return;
}
}
// response:
addCommand();
addRequestSequence();
addSuccess(true);
addRunning();
}
};
class V4DisconnectRequest: public V4CommandHandler
{
public:
V4DisconnectRequest(): V4CommandHandler(QStringLiteral("disconnect")) {}
void handleRequest() override
{
debugService->debuggerAgent.removeAllBreakPoints();
debugService->debuggerAgent.resumeAll();
// response:
addCommand();
addRequestSequence();
addSuccess(true);
addRunning();
}
};
class V4SetExceptionBreakRequest: public V4CommandHandler
{
public:
V4SetExceptionBreakRequest(): V4CommandHandler(QStringLiteral("setexceptionbreak")) {}
void handleRequest() override
{
bool wasEnabled = debugService->debuggerAgent.breakOnThrow();
//decypher the payload:
QJsonObject arguments = req.value(QLatin1String("arguments")).toObject();
QString type = arguments.value(QLatin1String("type")).toString();
bool enabled = arguments.value(QLatin1String("number")).toBool(!wasEnabled);
if (type == QLatin1String("all")) {
// that's fine
} else if (type == QLatin1String("uncaught")) {
createErrorResponse(QStringLiteral("breaking only on uncaught exceptions is not supported yet"));
return;
} else {
createErrorResponse(QStringLiteral("invalid type for break on exception"));
return;
}
// do it:
debugService->debuggerAgent.setBreakOnThrow(enabled);
QJsonObject body;
body[QLatin1String("type")] = type;
body[QLatin1String("enabled")] = debugService->debuggerAgent.breakOnThrow();
// response:
addBody(body);
addRunning();
addSuccess(true);
addRequestSequence();
addCommand();
}
};
class V4ScriptsRequest: public V4CommandHandler
{
public:
V4ScriptsRequest(): V4CommandHandler(QStringLiteral("scripts")) {}
void handleRequest() override
{
//decypher the payload:
QJsonObject arguments = req.value(QLatin1String("arguments")).toObject();
int types = arguments.value(QLatin1String("types")).toInt(-1);
if (types < 0 || types > 7) {
createErrorResponse(QStringLiteral("invalid types value in scripts command"));
return;
} else if (types != 4) {
createErrorResponse(QStringLiteral("unsupported types value in scripts command"));
return;
}
// do it:
QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
if (!debugger) {
createErrorResponse(QStringLiteral("Debugger has to be paused to retrieve scripts."));
return;
}
GatherSourcesJob job(debugger->engine());
debugger->runInEngine(&job);
QJsonArray body;
for (const QString &source : job.result()) {
QJsonObject src;
src[QLatin1String("name")] = source;
src[QLatin1String("scriptType")] = 4;
body.append(src);
}
addSuccess(true);
addRunning();
addBody(body);
addCommand();
addRequestSequence();
}
};
// Request:
// {
// "seq": 4,
// "type": "request",
// "command": "evaluate",
// "arguments": {
// "expression": "a",
// "frame": 0
// }
// }
//
// Response:
// {
// "body": {
// "handle": 3,
// "type": "number",
// "value": 1
// },
// "command": "evaluate",
// "refs": [],
// "request_seq": 4,
// "running": false,
// "seq": 5,
// "success": true,
// "type": "response"
// }
//
// The "value" key in "body" is the result of evaluating the expression in the request.
class V4EvaluateRequest: public V4CommandHandler
{
public:
V4EvaluateRequest(): V4CommandHandler(QStringLiteral("evaluate")) {}
void handleRequest() override
{
QJsonObject arguments = req.value(QLatin1String("arguments")).toObject();
QString expression = arguments.value(QLatin1String("expression")).toString();
int context = arguments.value(QLatin1String("context")).toInt(-1);
int frame = -1;
QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
if (!debugger) {
const QList<QV4Debugger *> &debuggers = debugService->debuggerAgent.debuggers();
if (debuggers.count() > 1) {
createErrorResponse(QStringLiteral("Cannot evaluate expressions if multiple debuggers are running and none is paused"));
return;
} else if (debuggers.count() == 0) {
createErrorResponse(QStringLiteral("No debuggers available to evaluate expressions"));
return;
}
debugger = debuggers.first();
} else {
frame = arguments.value(QLatin1String("frame")).toInt(0);
}
ExpressionEvalJob job(debugger->engine(), frame, context, expression,
debugger->collector());
debugger->runInEngine(&job);
if (job.hasExeption()) {
createErrorResponse(job.exceptionMessage());
} else {
addCommand();
addRequestSequence();
addSuccess(true);
addRunning();
addBody(job.returnValue());
}
}
};
} // anonymous namespace
void QV4DebugServiceImpl::addHandler(V4CommandHandler* handler)
{
handlers[handler->command()] = handler;
}
V4CommandHandler *QV4DebugServiceImpl::v4CommandHandler(const QString &command) const
{
V4CommandHandler *handler = handlers.value(command, 0);
if (handler)
return handler;
else
return unknownV4CommandHandler.data();
}
QV4DebugServiceImpl::QV4DebugServiceImpl(QObject *parent) :
QQmlConfigurableDebugService<QV4DebugService>(1, parent),
debuggerAgent(this), theSelectedFrame(0),
unknownV4CommandHandler(new UnknownV4CommandHandler)
{
addHandler(new V4VersionRequest);
addHandler(new V4SetBreakPointRequest);
addHandler(new V4ClearBreakPointRequest);
addHandler(new V4ChangeBreakPointRequest);
addHandler(new V4BacktraceRequest);
addHandler(new V4FrameRequest);
addHandler(new V4ScopeRequest);
addHandler(new V4LookupRequest);
addHandler(new V4ContinueRequest);
addHandler(new V4DisconnectRequest);
addHandler(new V4SetExceptionBreakRequest);
addHandler(new V4ScriptsRequest);
addHandler(new V4EvaluateRequest);
}
QV4DebugServiceImpl::~QV4DebugServiceImpl()
{
qDeleteAll(handlers);
}
void QV4DebugServiceImpl::engineAdded(QJSEngine *engine)
{
QMutexLocker lock(&m_configMutex);
if (engine) {
QV4::ExecutionEngine *ee = engine->handle();
if (QQmlDebugConnector *server = QQmlDebugConnector::instance()) {
if (ee) {
QV4Debugger *debugger = new QV4Debugger(ee);
if (state() == Enabled)
ee->setDebugger(debugger);
debuggerAgent.addDebugger(debugger);
debuggerAgent.moveToThread(server->thread());
}
}
}
QQmlConfigurableDebugService<QV4DebugService>::engineAdded(engine);
}
void QV4DebugServiceImpl::engineAboutToBeRemoved(QJSEngine *engine)
{
QMutexLocker lock(&m_configMutex);
if (engine){
const QV4::ExecutionEngine *ee = engine->handle();
if (ee) {
QV4Debugger *debugger = qobject_cast<QV4Debugger *>(ee->debugger());
if (debugger)
debuggerAgent.removeDebugger(debugger);
}
}
QQmlConfigurableDebugService<QV4DebugService>::engineAboutToBeRemoved(engine);
}
void QV4DebugServiceImpl::stateAboutToBeChanged(State state)
{
QMutexLocker lock(&m_configMutex);
if (state == Enabled) {
const auto debuggers = debuggerAgent.debuggers();
for (QV4Debugger *debugger : debuggers) {
QV4::ExecutionEngine *ee = debugger->engine();
if (!ee->debugger())
ee->setDebugger(debugger);
}
}
QQmlConfigurableDebugService<QV4DebugService>::stateAboutToBeChanged(state);
}
void QV4DebugServiceImpl::signalEmitted(const QString &signal)
{
//This function is only called by QQmlBoundSignal
//only if there is a slot connected to the signal. Hence, there
//is no need for additional check.
//Parse just the name and remove the class info
//Normalize to Lower case.
QString signalName = signal.left(signal.indexOf(QLatin1Char('('))).toLower();
for (const QString &signal : qAsConst(breakOnSignals)) {
if (signal == signalName) {
// TODO: pause debugger
break;
}
}
}
void QV4DebugServiceImpl::messageReceived(const QByteArray &message)
{
QMutexLocker lock(&m_configMutex);
QQmlDebugPacket ms(message);
QByteArray header;
ms >> header;
TRACE_PROTOCOL(qDebug() << "received message with header" << header);
if (header == "V8DEBUG") {
QByteArray type;
QByteArray payload;
ms >> type >> payload;
TRACE_PROTOCOL(qDebug() << "... type:" << type);
if (type == V4_CONNECT) {
QJsonObject parameters = QJsonDocument::fromJson(payload).object();
Q_UNUSED(parameters); // For future protocol changes
emit messageToClient(name(), packMessage(type));
stopWaiting();
} else if (type == V4_PAUSE) {
debuggerAgent.pauseAll();
sendSomethingToSomebody(type);
} else if (type == V4_BREAK_ON_SIGNAL) {
QByteArray signal;
bool enabled;
ms >> signal >> enabled;
//Normalize to lower case.
QString signalName(QString::fromUtf8(signal).toLower());
if (enabled)
breakOnSignals.append(signalName);
else
breakOnSignals.removeOne(signalName);
} else if (type == "v8request") {
handleV4Request(payload);
} else if (type == V4_DISCONNECT) {
TRACE_PROTOCOL(qDebug() << "... payload:" << payload.constData());
handleV4Request(payload);
} else {
sendSomethingToSomebody(type, 0);
}
}
}
void QV4DebugServiceImpl::sendSomethingToSomebody(const char *type, int magicNumber)
{
QQmlDebugPacket rs;
rs << QByteArray(type)
<< QByteArray::number(int(version())) << QByteArray::number(magicNumber);
emit messageToClient(name(), packMessage(type, rs.data()));
}
void QV4DebugServiceImpl::handleV4Request(const QByteArray &payload)
{
TRACE_PROTOCOL(qDebug() << "v8request, payload:" << payload.constData());
QJsonDocument request = QJsonDocument::fromJson(payload);
QJsonObject o = request.object();
QJsonValue type = o.value(QLatin1String("type"));
if (type.toString() == QLatin1String("request")) {
QJsonValue command = o.value(QLatin1String("command"));
V4CommandHandler *h = v4CommandHandler(command.toString());
if (h)
h->handle(o, this);
}
}
QByteArray QV4DebugServiceImpl::packMessage(const QByteArray &command, const QByteArray &message)
{
QQmlDebugPacket rs;
static const QByteArray cmd("V8DEBUG");
rs << cmd << command << message;
return rs.data();
}
void QV4DebugServiceImpl::send(QJsonObject v4Payload)
{
v4Payload[QLatin1String("seq")] = sequence++;
QJsonDocument doc;
doc.setObject(v4Payload);
#ifdef NO_PROTOCOL_TRACING
QByteArray responseData = doc.toJson(QJsonDocument::Compact);
#else
QByteArray responseData = doc.toJson(QJsonDocument::Indented);
#endif
TRACE_PROTOCOL(qDebug() << "sending response for:" << responseData.constData() << endl);
emit messageToClient(name(), packMessage("v8message", responseData));
}
void QV4DebugServiceImpl::selectFrame(int frameNr)
{
theSelectedFrame = frameNr;
}
int QV4DebugServiceImpl::selectedFrame() const
{
return theSelectedFrame;
}
QT_END_NAMESPACE
#include "moc_qv4debugservice.cpp"