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"