Revision control

++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant
++
++ #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.size() > 1) {
++ createErrorResponse(QStringLiteral("Cannot lookup values if multiple debuggers are running and none is paused"));
++ return;
++ } else if (debuggers.size() == 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.size() > 1) {
++ createErrorResponse(QStringLiteral("Cannot evaluate expressions if multiple debuggers are running and none is paused"));
++ return;
++ } else if (debuggers.size() == 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) {
++ const auto debuggers = debuggerAgent.debuggers();
++ for (QV4Debugger *debugger : debuggers) {
++ if (debugger->engine() == ee) {
++ debuggerAgent.removeDebugger(debugger);
++ if (ee->debugger() != debugger)
++ delete debugger;
++ break;
++ }
++ }
++ }
++ }
++ 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 : std::as_const(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"