File indexing completed on 2024-05-12 05:51:05

0001 /*
0002     SPDX-FileCopyrightText: 2022 Héctor Mesa Jiménez <wmj.py@gmx.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 #include "client.h"
0007 #include <QJsonArray>
0008 #include <QJsonDocument>
0009 #include <limits>
0010 
0011 #include "dap/bus_selector.h"
0012 #include "dap/settings.h"
0013 #include "dapclient_debug.h"
0014 
0015 #include "messages.h"
0016 
0017 namespace dap
0018 {
0019 constexpr int MAX_HEADER_SIZE = 1 << 16;
0020 
0021 template<typename T>
0022 inline Client::ResponseHandler make_response_handler(void (T::*member)(const Response &response, const QJsonValue &request), T *object)
0023 {
0024     return [object, member](const Response &response, const QJsonValue &request) {
0025         return (object->*member)(response, request);
0026     };
0027 }
0028 
0029 Client::Client(const settings::ProtocolSettings &protocolSettings, Bus *bus, QObject *parent)
0030     : QObject(parent)
0031     , m_bus(bus)
0032     , m_managedBus(false)
0033     , m_protocol(protocolSettings)
0034     , m_launchCommand(extractCommand(protocolSettings.launchRequest))
0035 {
0036     bind();
0037 }
0038 
0039 Client::Client(const settings::ClientSettings &clientSettings, QObject *parent)
0040     : QObject(parent)
0041     , m_managedBus(true)
0042     , m_protocol(clientSettings.protocolSettings)
0043     , m_launchCommand(extractCommand(clientSettings.protocolSettings.launchRequest))
0044 {
0045     m_bus = createBus(clientSettings.busSettings);
0046     m_bus->setParent(this);
0047     bind();
0048 }
0049 
0050 Client::~Client()
0051 {
0052     detach();
0053 }
0054 
0055 void Client::bind()
0056 {
0057     connect(m_bus, &Bus::readyRead, this, &Client::read);
0058     connect(m_bus, &Bus::running, this, &Client::start);
0059     connect(m_bus, &Bus::closed, this, &Client::finished);
0060     if (m_protocol.redirectStderr)
0061         connect(m_bus, &Bus::serverOutput, this, &Client::onServerOutput);
0062     if (m_protocol.redirectStdout)
0063         connect(m_bus, &Bus::processOutput, this, &Client::onProcessOutput);
0064 }
0065 
0066 void Client::detach()
0067 {
0068     if (!m_bus)
0069         return;
0070 
0071     disconnect(m_bus);
0072     if (m_managedBus) {
0073         m_bus->close();
0074         m_bus->deleteLater();
0075         m_bus = nullptr;
0076     }
0077 }
0078 
0079 bool Client::isServerConnected() const
0080 {
0081     return (m_state != State::None) && (m_state != State::Failed) && (m_bus->state() == Bus::State::Running);
0082 }
0083 
0084 bool Client::supportsTerminate() const
0085 {
0086     return m_adapterCapabilities.supportsTerminateRequest && (m_protocol.launchRequest[DAP_COMMAND].toString() == DAP_LAUNCH);
0087 }
0088 
0089 /*
0090  * fields:
0091  *  seq: number;
0092  *  type: 'request' | 'response' | 'event' | string;
0093  *
0094  * extensions:
0095  *  Request
0096  *  Response
0097  *  Event
0098  */
0099 void Client::processProtocolMessage(const QJsonObject &msg)
0100 {
0101     const auto type = msg[DAP_TYPE].toString();
0102 
0103     if (DAP_RESPONSE == type) {
0104         processResponse(msg);
0105     } else if (DAP_EVENT == type) {
0106         processEvent(msg);
0107     } else {
0108         qCWarning(DAPCLIENT) << "unknown, empty or unexpected ProtocolMessage::" << DAP_TYPE << " (" << type << ")";
0109     }
0110 }
0111 
0112 /*
0113  * extends ProtocolMessage
0114  *
0115  * fields:
0116  *  request_seq: number;
0117  *  success: boolean;
0118  *  command: string
0119  *  message?: 'cancelled' | string;
0120  *  body?: any;
0121  *
0122  * extensions:
0123  *  ErrorResponse
0124  */
0125 void Client::processResponse(const QJsonObject &msg)
0126 {
0127     const Response response(msg);
0128     // check sequence
0129     if ((response.request_seq < 0) || !m_requests.contains(response.request_seq)) {
0130         qCWarning(DAPCLIENT) << "unexpected requested seq in response";
0131         return;
0132     }
0133 
0134     const auto request = m_requests.take(response.request_seq);
0135 
0136     // check response
0137     if (response.command != std::get<0>(request)) {
0138         qCWarning(DAPCLIENT) << "unexpected command in response: " << response.command << " (expected: " << std::get<0>(request) << ")";
0139     }
0140     if (response.isCancelled()) {
0141         qCWarning(DAPCLIENT) << "request cancelled: " << response.command;
0142     }
0143 
0144     if (!response.success) {
0145         Q_EMIT errorResponse(response.message, response.errorBody);
0146     }
0147     std::get<2>(request)(response, std::get<1>(request));
0148 }
0149 
0150 void Client::processResponseInitialize(const Response &response, const QJsonValue &)
0151 {
0152     if (m_state != State::Initializing) {
0153         qCWarning(DAPCLIENT) << "unexpected initialize response";
0154         setState(State::None);
0155         return;
0156     }
0157 
0158     if (!response.success && response.isCancelled()) {
0159         qCWarning(DAPCLIENT) << "InitializeResponse error: " << response.message;
0160         if (response.errorBody) {
0161             qCWarning(DAPCLIENT) << "error" << response.errorBody->id << response.errorBody->format;
0162         }
0163         setState(State::None);
0164         return;
0165     }
0166 
0167     // get server capabilities
0168     m_adapterCapabilities = Capabilities(response.body.toObject());
0169     Q_EMIT capabilitiesReceived(m_adapterCapabilities);
0170 
0171     requestLaunchCommand();
0172 }
0173 
0174 void Client::processEvent(const QJsonObject &msg)
0175 {
0176     const QString event = msg[DAP_EVENT].toString();
0177     const auto body = msg[DAP_BODY].toObject();
0178 
0179     if (QStringLiteral("initialized") == event) {
0180         processEventInitialized();
0181     } else if (QStringLiteral("terminated") == event) {
0182         processEventTerminated();
0183     } else if (QStringLiteral("exited") == event) {
0184         processEventExited(body);
0185     } else if (DAP_OUTPUT == event) {
0186         processEventOutput(body);
0187     } else if (QStringLiteral("process") == event) {
0188         processEventProcess(body);
0189     } else if (QStringLiteral("thread") == event) {
0190         processEventThread(body);
0191     } else if (QStringLiteral("stopped") == event) {
0192         processEventStopped(body);
0193     } else if (QStringLiteral("module") == event) {
0194         processEventModule(body);
0195     } else if (QStringLiteral("continued") == event) {
0196         processEventContinued(body);
0197     } else if (DAP_BREAKPOINT == event) {
0198         processEventBreakpoint(body);
0199     } else {
0200         qCWarning(DAPCLIENT) << "unsupported event: " << event;
0201     }
0202 }
0203 
0204 void Client::processEventInitialized()
0205 {
0206     if ((m_state != State::Initializing)) {
0207         qCWarning(DAPCLIENT) << "unexpected initialized event";
0208         return;
0209     }
0210     setState(State::Initialized);
0211 }
0212 
0213 void Client::processEventTerminated()
0214 {
0215     setState(State::Terminated);
0216 }
0217 
0218 void Client::processEventExited(const QJsonObject &body)
0219 {
0220     const int exitCode = body[QStringLiteral("exitCode")].toInt(-1);
0221     Q_EMIT debuggeeExited(exitCode);
0222 }
0223 
0224 void Client::processEventOutput(const QJsonObject &body)
0225 {
0226     Q_EMIT outputProduced(Output(body));
0227 }
0228 
0229 void Client::processEventProcess(const QJsonObject &body)
0230 {
0231     Q_EMIT debuggingProcess(ProcessInfo(body));
0232 }
0233 
0234 void Client::processEventThread(const QJsonObject &body)
0235 {
0236     Q_EMIT threadChanged(ThreadEvent(body));
0237 }
0238 
0239 void Client::processEventStopped(const QJsonObject &body)
0240 {
0241     Q_EMIT debuggeeStopped(StoppedEvent(body));
0242 }
0243 
0244 void Client::processEventModule(const QJsonObject &body)
0245 {
0246     Q_EMIT moduleChanged(ModuleEvent(body));
0247 }
0248 
0249 void Client::processEventContinued(const QJsonObject &body)
0250 {
0251     Q_EMIT debuggeeContinued(ContinuedEvent(body));
0252 }
0253 
0254 void Client::processEventBreakpoint(const QJsonObject &body)
0255 {
0256     Q_EMIT breakpointChanged(BreakpointEvent(body));
0257 }
0258 
0259 void Client::processResponseConfigurationDone(const Response &response, const QJsonValue &)
0260 {
0261     if (response.success) {
0262         m_configured = true;
0263         Q_EMIT configured();
0264         checkRunning();
0265     }
0266 }
0267 
0268 void Client::processResponseLaunch(const Response &response, const QJsonValue &)
0269 {
0270     if (response.success) {
0271         m_launched = true;
0272         Q_EMIT launched();
0273         checkRunning();
0274     } else {
0275         setState(State::Failed);
0276     }
0277 }
0278 
0279 void Client::processResponseThreads(const Response &response, const QJsonValue &)
0280 {
0281     if (response.success) {
0282         Q_EMIT threads(Thread::parseList(response.body.toObject()[DAP_THREADS].toArray()));
0283     } else {
0284         Q_EMIT threads(QList<Thread>());
0285     }
0286 }
0287 
0288 void Client::processResponseStackTrace(const Response &response, const QJsonValue &request)
0289 {
0290     const int threadId = request.toObject()[DAP_THREAD_ID].toInt();
0291     if (response.success) {
0292         Q_EMIT stackTrace(threadId, StackTraceInfo(response.body.toObject()));
0293     } else {
0294         Q_EMIT stackTrace(threadId, StackTraceInfo());
0295     }
0296 }
0297 
0298 void Client::processResponseScopes(const Response &response, const QJsonValue &request)
0299 {
0300     const int frameId = request.toObject()[DAP_FRAME_ID].toInt();
0301     if (response.success) {
0302         Q_EMIT scopes(frameId, Scope::parseList(response.body.toObject()[DAP_SCOPES].toArray()));
0303     } else {
0304         Q_EMIT scopes(frameId, QList<Scope>());
0305     }
0306 }
0307 
0308 void Client::processResponseVariables(const Response &response, const QJsonValue &request)
0309 {
0310     const int variablesReference = request.toObject()[DAP_VARIABLES_REFERENCE].toInt();
0311     if (response.success) {
0312         Q_EMIT variables(variablesReference, Variable::parseList(response.body.toObject()[DAP_VARIABLES].toArray()));
0313     } else {
0314         Q_EMIT variables(variablesReference, QList<Variable>());
0315     }
0316 }
0317 
0318 void Client::processResponseModules(const Response &response, const QJsonValue &)
0319 {
0320     if (response.success) {
0321         Q_EMIT modules(ModulesInfo(response.body.toObject()));
0322     } else {
0323         Q_EMIT modules(ModulesInfo());
0324     }
0325 }
0326 
0327 void Client::processResponseNext(const Response &response, const QJsonValue &request)
0328 {
0329     if (response.success) {
0330         Q_EMIT debuggeeContinued(ContinuedEvent(request.toObject()[DAP_THREAD_ID].toInt(), !response.body.toObject()[DAP_SINGLE_THREAD].toBool(false)));
0331     }
0332 }
0333 
0334 void Client::processResponseContinue(const Response &response, const QJsonValue &request)
0335 {
0336     if (response.success) {
0337         Q_EMIT debuggeeContinued(ContinuedEvent(request.toObject()[DAP_THREAD_ID].toInt(), response.body.toObject()[DAP_ALL_THREADS_CONTINUED].toBool(true)));
0338     }
0339 }
0340 
0341 void Client::processResponsePause(const Response &, const QJsonValue &)
0342 {
0343 }
0344 
0345 void Client::processResponseTerminate(const Response &, const QJsonValue &)
0346 {
0347 }
0348 
0349 void Client::processResponseDisconnect(const Response &response, const QJsonValue &)
0350 {
0351     if (response.success) {
0352         Q_EMIT serverDisconnected();
0353     }
0354 }
0355 
0356 void Client::processResponseSource(const Response &response, const QJsonValue &request)
0357 {
0358     const auto req = request.toObject();
0359     const auto path = req[DAP_SOURCE].toObject()[DAP_PATH].toString();
0360     const auto reference = req[DAP_SOURCE_REFERENCE].toInt(0);
0361     if (response.success) {
0362         Q_EMIT sourceContent(path, reference, SourceContent(response.body.toObject()));
0363     } else {
0364         // empty
0365         Q_EMIT sourceContent(path, reference, SourceContent());
0366     }
0367 }
0368 
0369 void Client::processResponseSetBreakpoints(const Response &response, const QJsonValue &request)
0370 {
0371     const auto source = Source(request.toObject()[DAP_SOURCE].toObject());
0372     if (response.success) {
0373         const auto resp = response.body.toObject();
0374         if (resp.contains(DAP_BREAKPOINTS)) {
0375             QList<Breakpoint> breakpoints;
0376             for (const auto &item : resp[DAP_BREAKPOINTS].toArray()) {
0377                 breakpoints.append(Breakpoint(item.toObject()));
0378             }
0379             Q_EMIT sourceBreakpoints(source.path, source.sourceReference.value_or(0), breakpoints);
0380         } else {
0381             QList<Breakpoint> breakpoints;
0382             for (const auto &item : resp[DAP_LINES].toArray()) {
0383                 breakpoints.append(Breakpoint(item.toInt()));
0384             }
0385             Q_EMIT sourceBreakpoints(source.path, source.sourceReference.value_or(0), breakpoints);
0386         }
0387     } else {
0388         Q_EMIT sourceBreakpoints(source.path, source.sourceReference.value_or(0), std::nullopt);
0389     }
0390 }
0391 
0392 void Client::processResponseEvaluate(const Response &response, const QJsonValue &request)
0393 {
0394     const auto &expression = request.toObject()[DAP_EXPRESSION].toString();
0395     if (response.success) {
0396         Q_EMIT expressionEvaluated(expression, EvaluateInfo(response.body.toObject()));
0397     } else {
0398         Q_EMIT expressionEvaluated(expression, std::nullopt);
0399     }
0400 }
0401 
0402 void Client::processResponseGotoTargets(const Response &response, const QJsonValue &request)
0403 {
0404     const auto &req = request.toObject();
0405     const auto source = Source(req[DAP_SOURCE].toObject());
0406     const int line = req[DAP_LINE].toInt();
0407     if (response.success) {
0408         Q_EMIT gotoTargets(source, line, GotoTarget::parseList(response.body.toObject()[QStringLiteral("targets")].toArray()));
0409     } else {
0410         Q_EMIT gotoTargets(source, line, QList<GotoTarget>());
0411     }
0412 }
0413 
0414 void Client::setState(const State &state)
0415 {
0416     if (state != m_state) {
0417         m_state = state;
0418         Q_EMIT stateChanged(m_state);
0419 
0420         switch (m_state) {
0421         case State::Initialized:
0422             Q_EMIT initialized();
0423             checkRunning();
0424             break;
0425         case State::Running:
0426             Q_EMIT debuggeeRunning();
0427             break;
0428         case State::Terminated:
0429             Q_EMIT debuggeeTerminated();
0430             break;
0431         case State::Failed:
0432             Q_EMIT failed();
0433             break;
0434         default:;
0435         }
0436     }
0437 }
0438 
0439 int Client::sequenceNumber()
0440 {
0441     const int seq = m_seq;
0442     if (m_seq == std::numeric_limits<int>::max()) {
0443         m_seq = 0;
0444     } else {
0445         ++m_seq;
0446     }
0447     return seq;
0448 }
0449 
0450 void Client::write(const QJsonObject &msg)
0451 {
0452     const auto payload = QJsonDocument(msg).toJson();
0453 
0454     qCDebug(DAPCLIENT) << "--> " << msg;
0455 
0456     // write header
0457     m_bus->write(DAP_TPL_HEADER_FIELD.arg(QLatin1String(DAP_CONTENT_LENGTH)).arg(payload.size()).toLatin1());
0458     m_bus->write(DAP_SEP);
0459     // write payload
0460     m_bus->write(payload);
0461 }
0462 
0463 /*
0464  * command: str
0465  * arguments?: any
0466  */
0467 QJsonObject Client::makeRequest(const QString &command, const QJsonValue &arguments, const ResponseHandler &handler)
0468 {
0469     const int seq = sequenceNumber();
0470     QJsonObject message;
0471     // ProtocolMessage
0472     message[DAP_SEQ] = seq;
0473     message[DAP_TYPE] = DAP_REQUEST;
0474     // Request
0475     message[DAP_COMMAND] = command;
0476     if (!arguments.isUndefined()) {
0477         message[DAP_ARGUMENTS] = arguments;
0478     }
0479     m_requests[seq] = std::make_tuple(command, arguments, handler);
0480 
0481     return message;
0482 }
0483 
0484 void Client::requestInitialize()
0485 {
0486     const QJsonObject capabilities{// TODO clientID?: string
0487                                    // TODO clientName?: string
0488                                    {QStringLiteral("locale"), m_protocol.locale},
0489                                    {DAP_ADAPTER_ID, QStringLiteral("qdap")},
0490                                    {DAP_LINES_START_AT1, m_protocol.linesStartAt1},
0491                                    {DAP_COLUMNS_START_AT2, m_protocol.columnsStartAt1},
0492                                    {DAP_PATH, (m_protocol.pathFormatURI ? DAP_URI : DAP_PATH)},
0493                                    {DAP_SUPPORTS_VARIABLE_TYPE, true},
0494                                    {DAP_SUPPORTS_VARIABLE_PAGING, false},
0495                                    {DAP_SUPPORTS_RUN_IN_TERMINAL_REQUEST, false},
0496                                    {DAP_SUPPORTS_MEMORY_REFERENCES, false},
0497                                    {DAP_SUPPORTS_PROGRESS_REPORTING, false},
0498                                    {DAP_SUPPORTS_INVALIDATED_EVENT, false},
0499                                    {DAP_SUPPORTS_MEMORY_EVENT, false}};
0500 
0501     setState(State::Initializing);
0502     this->write(makeRequest(DAP_INITIALIZE, capabilities, make_response_handler(&Client::processResponseInitialize, this)));
0503 }
0504 
0505 void Client::requestConfigurationDone()
0506 {
0507     if (m_state != State::Initialized) {
0508         qCWarning(DAPCLIENT) << "trying to configure in an unexpected status";
0509         return;
0510     }
0511 
0512     if (!m_adapterCapabilities.supportsConfigurationDoneRequest) {
0513         Q_EMIT configured();
0514         return;
0515     }
0516 
0517     this->write(makeRequest(QStringLiteral("configurationDone"), QJsonObject{}, make_response_handler(&Client::processResponseConfigurationDone, this)));
0518 }
0519 
0520 void Client::requestThreads()
0521 {
0522     this->write(makeRequest(DAP_THREADS, QJsonObject{}, make_response_handler(&Client::processResponseThreads, this)));
0523 }
0524 
0525 void Client::requestStackTrace(int threadId, int startFrame, int levels)
0526 {
0527     const QJsonObject arguments{{DAP_THREAD_ID, threadId}, {QStringLiteral("startFrame"), startFrame}, {QStringLiteral("levels"), levels}};
0528 
0529     this->write(makeRequest(QStringLiteral("stackTrace"), arguments, make_response_handler(&Client::processResponseStackTrace, this)));
0530 }
0531 
0532 void Client::requestScopes(int frameId)
0533 {
0534     const QJsonObject arguments{{DAP_FRAME_ID, frameId}};
0535 
0536     this->write(makeRequest(DAP_SCOPES, arguments, make_response_handler(&Client::processResponseScopes, this)));
0537 }
0538 
0539 void Client::requestVariables(int variablesReference, Variable::Type filter, int start, int count)
0540 {
0541     QJsonObject arguments{
0542         {DAP_VARIABLES_REFERENCE, variablesReference},
0543         {DAP_START, start},
0544         {DAP_COUNT, count},
0545     };
0546 
0547     switch (filter) {
0548     case Variable::Type::Indexed:
0549         arguments[DAP_FILTER] = QStringLiteral("indexed");
0550         break;
0551     case Variable::Type::Named:
0552         arguments[DAP_FILTER] = QStringLiteral("named");
0553         break;
0554     default:;
0555     }
0556 
0557     this->write(makeRequest(DAP_VARIABLES, arguments, make_response_handler(&Client::processResponseVariables, this)));
0558 }
0559 
0560 void Client::requestModules(int start, int count)
0561 {
0562     this->write(makeRequest(DAP_MODULES, QJsonObject{{DAP_START, start}, {DAP_COUNT, count}}, make_response_handler(&Client::processResponseModules, this)));
0563 }
0564 
0565 void Client::requestNext(int threadId, bool singleThread)
0566 {
0567     QJsonObject arguments{{DAP_THREAD_ID, threadId}};
0568     if (singleThread) {
0569         arguments[DAP_SINGLE_THREAD] = true;
0570     }
0571     this->write(makeRequest(QStringLiteral("next"), arguments, make_response_handler(&Client::processResponseNext, this)));
0572 }
0573 
0574 void Client::requestStepIn(int threadId, bool singleThread)
0575 {
0576     QJsonObject arguments{{DAP_THREAD_ID, threadId}};
0577     if (singleThread) {
0578         arguments[DAP_SINGLE_THREAD] = true;
0579     }
0580     this->write(makeRequest(QStringLiteral("stepIn"), arguments, make_response_handler(&Client::processResponseNext, this)));
0581 }
0582 
0583 void Client::requestStepOut(int threadId, bool singleThread)
0584 {
0585     QJsonObject arguments{{DAP_THREAD_ID, threadId}};
0586     if (singleThread) {
0587         arguments[DAP_SINGLE_THREAD] = true;
0588     }
0589     this->write(makeRequest(QStringLiteral("stepOut"), arguments, make_response_handler(&Client::processResponseNext, this)));
0590 }
0591 
0592 void Client::requestGoto(int threadId, int targetId)
0593 {
0594     const QJsonObject arguments{{DAP_THREAD_ID, threadId}, {DAP_TARGET_ID, targetId}};
0595 
0596     this->write(makeRequest(QStringLiteral("goto"), arguments, make_response_handler(&Client::processResponseNext, this)));
0597 }
0598 
0599 void Client::requestContinue(int threadId, bool singleThread)
0600 {
0601     QJsonObject arguments{{DAP_THREAD_ID, threadId}};
0602     if (singleThread) {
0603         arguments[DAP_SINGLE_THREAD] = true;
0604     }
0605     this->write(makeRequest(QStringLiteral("continue"), arguments, make_response_handler(&Client::processResponseContinue, this)));
0606 }
0607 
0608 void Client::requestPause(int threadId)
0609 {
0610     const QJsonObject arguments{{DAP_THREAD_ID, threadId}};
0611 
0612     this->write(makeRequest(QStringLiteral("pause"), arguments, make_response_handler(&Client::processResponsePause, this)));
0613 }
0614 
0615 void Client::requestTerminate(bool restart)
0616 {
0617     QJsonObject arguments;
0618     if (restart) {
0619         arguments[QStringLiteral("restart")] = true;
0620     }
0621 
0622     this->write(makeRequest(QStringLiteral("terminate"), arguments, make_response_handler(&Client::processResponseTerminate, this)));
0623 }
0624 
0625 void Client::requestDisconnect(bool restart)
0626 {
0627     QJsonObject arguments;
0628     if (restart) {
0629         arguments[QStringLiteral("restart")] = true;
0630     }
0631 
0632     this->write(makeRequest(QStringLiteral("disconnect"), arguments, make_response_handler(&Client::processResponseDisconnect, this)));
0633 }
0634 
0635 void Client::requestSource(const Source &source)
0636 {
0637     QJsonObject arguments{{DAP_SOURCE_REFERENCE, source.sourceReference.value_or(0)}};
0638     const QJsonObject sourceArg{{DAP_SOURCE_REFERENCE, source.sourceReference.value_or(0)}, {DAP_PATH, source.path}};
0639 
0640     arguments[DAP_SOURCE] = sourceArg;
0641 
0642     this->write(makeRequest(DAP_SOURCE, arguments, make_response_handler(&Client::processResponseSource, this)));
0643 }
0644 
0645 void Client::requestSetBreakpoints(const QString &path, const QList<SourceBreakpoint> breakpoints, bool sourceModified)
0646 {
0647     requestSetBreakpoints(Source(path), breakpoints, sourceModified);
0648 }
0649 
0650 void Client::requestSetBreakpoints(const Source &source, const QList<SourceBreakpoint> breakpoints, bool sourceModified)
0651 {
0652     QJsonArray bpoints;
0653     for (const auto &item : breakpoints) {
0654         bpoints.append(item.toJson());
0655     }
0656     QJsonObject arguments{{DAP_SOURCE, source.toJson()}, {DAP_BREAKPOINTS, bpoints}, {QStringLiteral("sourceModified"), sourceModified}};
0657 
0658     this->write(makeRequest(QStringLiteral("setBreakpoints"), arguments, make_response_handler(&Client::processResponseSetBreakpoints, this)));
0659 }
0660 
0661 void Client::requestEvaluate(const QString &expression, const QString &context, std::optional<int> frameId)
0662 {
0663     QJsonObject arguments{{DAP_EXPRESSION, expression}};
0664     if (!context.isEmpty()) {
0665         arguments[DAP_CONTEXT] = context;
0666     }
0667     if (frameId) {
0668         arguments[DAP_FRAME_ID] = *frameId;
0669     }
0670 
0671     this->write(makeRequest(QStringLiteral("evaluate"), arguments, make_response_handler(&Client::processResponseEvaluate, this)));
0672 }
0673 
0674 void Client::requestWatch(const QString &expression, std::optional<int> frameId)
0675 {
0676     requestEvaluate(expression, QStringLiteral("watch"), frameId);
0677 }
0678 
0679 void Client::requestGotoTargets(const QString &path, const int line, const std::optional<int> column)
0680 {
0681     requestGotoTargets(Source(path), line, column);
0682 }
0683 
0684 void Client::requestGotoTargets(const Source &source, const int line, const std::optional<int> column)
0685 {
0686     QJsonObject arguments{{DAP_SOURCE, source.toJson()}, {DAP_LINE, line}};
0687     if (column) {
0688         arguments[DAP_COLUMN] = *column;
0689     }
0690 
0691     this->write(makeRequest(QStringLiteral("gotoTargets"), arguments, make_response_handler(&Client::processResponseGotoTargets, this)));
0692 }
0693 
0694 void Client::requestLaunchCommand()
0695 {
0696     if (m_state != State::Initializing) {
0697         qCWarning(DAPCLIENT) << "trying to launch in an unexpected state";
0698         return;
0699     }
0700     if (m_launchCommand.isNull() || m_launchCommand.isEmpty())
0701         return;
0702 
0703     this->write(makeRequest(m_launchCommand, m_protocol.launchRequest, make_response_handler(&Client::processResponseLaunch, this)));
0704 }
0705 
0706 void Client::checkRunning()
0707 {
0708     if (m_launched && m_configured && (m_state == State::Initialized)) {
0709         setState(State::Running);
0710     }
0711 }
0712 
0713 void Client::onServerOutput(const QString &message)
0714 {
0715     Q_EMIT outputProduced(dap::Output(message, dap::Output::Category::Console));
0716 }
0717 
0718 void Client::onProcessOutput(const QString &message)
0719 {
0720     Q_EMIT outputProduced(dap::Output(message, dap::Output::Category::Stdout));
0721 }
0722 
0723 QString Client::extractCommand(const QJsonObject &launchRequest)
0724 {
0725     const auto &command = launchRequest[DAP_COMMAND].toString();
0726     if ((command != DAP_LAUNCH) && (command != DAP_ATTACH)) {
0727         qCWarning(DAPCLIENT) << "unsupported request command: " << command;
0728         return QString();
0729     }
0730     return command;
0731 }
0732 
0733 void Client::read()
0734 {
0735     m_buffer.append(m_bus->read());
0736 
0737     while (true) {
0738         // read headers
0739         const auto info = readHeader();
0740         if (!info) {
0741             // incomplete header -> abort
0742             break; // PENDING
0743         }
0744         // read payload
0745         const auto data = m_buffer.mid(info->payloadStart, info->payloadLength);
0746         if (data.size() < info->payloadLength) {
0747             break; // PENDING
0748         }
0749         m_buffer.remove(0, info->payloadStart + info->payloadLength);
0750 
0751         // parse payload
0752         QJsonParseError jsonError;
0753         const auto message = QJsonDocument::fromJson(data, &jsonError);
0754         if ((jsonError.error != QJsonParseError::NoError) || message.isNull() || !message.isObject()) {
0755             qCWarning(DAPCLIENT) << "JSON bad format: " << jsonError.errorString();
0756             continue;
0757         }
0758 
0759         qDebug(DAPCLIENT) << "<-- " << message;
0760 
0761         // process message
0762         processProtocolMessage(message.object());
0763     }
0764 }
0765 
0766 std::optional<Client::HeaderInfo> Client::readHeader()
0767 {
0768     int length = -1;
0769     int start = 0;
0770     int end = -1;
0771 
0772     auto discardExploredBuffer = [this, length, start, end]() mutable {
0773         m_buffer.remove(0, end);
0774         length = end = -1;
0775         start = 0;
0776     };
0777 
0778     while (true) {
0779         end = m_buffer.indexOf(DAP_SEP, start);
0780         if (end < 0) {
0781             if (m_buffer.size() > MAX_HEADER_SIZE) {
0782                 m_buffer.clear();
0783             }
0784             length = -1;
0785             break; // PENDING
0786         }
0787 
0788         const auto header = m_buffer.mid(start, end - start);
0789         end += DAP_SEP_SIZE;
0790 
0791         // header block separator
0792         if (header.size() == 0) {
0793             if (length < 0) {
0794                 // unexpected end of header
0795                 qCWarning(DAPCLIENT) << "unexpected end of header block";
0796                 discardExploredBuffer();
0797                 continue;
0798             }
0799             break; // END HEADER (length>0, end>0)
0800         }
0801 
0802         // parse field
0803         const int sep = header.indexOf(":");
0804         if (sep < 0) {
0805             qCWarning(DAPCLIENT) << "cannot parse header field: " << header;
0806             discardExploredBuffer();
0807             continue; // CONTINUE HEADER
0808         }
0809 
0810         // parse content-length
0811         if (header.left(sep) == DAP_CONTENT_LENGTH) {
0812             bool ok = false;
0813             length = header.mid(sep + 1, header.size() - sep).toInt(&ok);
0814             if (!ok) {
0815                 qCWarning(DAPCLIENT) << "invalid value: " << header;
0816                 discardExploredBuffer();
0817                 continue; // CONTINUE HEADER
0818             }
0819         }
0820         start = end;
0821     }
0822 
0823     if (length < 0) {
0824         return std::nullopt;
0825     }
0826 
0827     return HeaderInfo{end, length};
0828 }
0829 
0830 void Client::start()
0831 {
0832     m_launched = false;
0833     m_configured = false;
0834     if (m_state != State::None) {
0835         qCWarning(DAPCLIENT) << "trying to re-start has no effect";
0836         return;
0837     }
0838     requestInitialize();
0839 }
0840 
0841 }
0842 
0843 #include "moc_client.cpp"