File indexing completed on 2024-05-12 05:51:06
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 0007 #include "entities.h" 0008 #include "messages.h" 0009 0010 #include <QFile> 0011 #include <QFileInfo> 0012 #include <QJsonArray> 0013 #include <QJsonObject> 0014 0015 std::optional<int> parseOptionalInt(const QJsonValue &value) 0016 { 0017 if (value.isNull() || value.isUndefined() || !value.isDouble()) { 0018 return std::nullopt; 0019 } 0020 return value.toInt(); 0021 } 0022 0023 std::optional<bool> parseOptionalBool(const QJsonValue &value) 0024 { 0025 if (value.isNull() || value.isUndefined() || !value.isBool()) { 0026 return std::nullopt; 0027 } 0028 return value.toBool(); 0029 } 0030 0031 std::optional<QString> parseOptionalString(const QJsonValue &value) 0032 { 0033 if (value.isNull() || value.isUndefined() || !value.isString()) { 0034 return std::nullopt; 0035 } 0036 return value.toString(); 0037 } 0038 0039 template<typename T> 0040 std::optional<T> parseOptionalObject(const QJsonValue &value) 0041 { 0042 if (value.isNull() || value.isUndefined() || !value.isObject()) { 0043 return std::nullopt; 0044 } 0045 return T(value.toObject()); 0046 } 0047 0048 std::optional<QHash<QString, QString>> parseOptionalStringMap(const QJsonValue &value) 0049 { 0050 if (value.isNull() || value.isUndefined() || !value.isObject()) { 0051 return std::nullopt; 0052 } 0053 const auto &dict = value.toObject(); 0054 QHash<QString, QString> map; 0055 for (auto it = dict.begin(); it != dict.end(); ++it) { 0056 map[it.key()] = it.value().toString(); 0057 } 0058 return map; 0059 } 0060 0061 template<typename T> 0062 QList<T> parseObjectList(const QJsonArray &array) 0063 { 0064 QList<T> out; 0065 for (const auto &item : array) { 0066 out << T(item.toObject()); 0067 } 0068 return out; 0069 } 0070 0071 std::optional<QList<int>> parseOptionalIntList(const QJsonValue &value) 0072 { 0073 if (value.isNull() || value.isUndefined() || !value.isArray()) { 0074 return std::nullopt; 0075 } 0076 QList<int> values; 0077 for (const auto &item : value.toArray()) { 0078 values.append(item.toInt()); 0079 } 0080 return values; 0081 } 0082 0083 template<typename T> 0084 QJsonArray toJsonArray(const QList<T> &items) 0085 { 0086 QJsonArray out; 0087 for (const auto &item : items) { 0088 out << item.toJson(); 0089 } 0090 return out; 0091 } 0092 0093 namespace dap 0094 { 0095 Message::Message(const QJsonObject &body) 0096 : id(body[DAP_ID].toInt()) 0097 , format(body[QStringLiteral("format")].toString()) 0098 , variables(parseOptionalStringMap(body[QStringLiteral("variables")])) 0099 , sendTelemetry(parseOptionalBool(body[QStringLiteral("sendTelemetry")])) 0100 , showUser(parseOptionalBool(body[QStringLiteral("showUser")])) 0101 , url(parseOptionalString(body[QStringLiteral("url")])) 0102 , urlLabel(parseOptionalString(body[QStringLiteral("urlLabel")])) 0103 { 0104 } 0105 0106 Response::Response(const QJsonObject &msg) 0107 : request_seq(msg[QStringLiteral("request_seq")].toInt(-1)) 0108 , success(msg[QStringLiteral("success")].toBool(false)) 0109 , command(msg[DAP_COMMAND].toString()) 0110 , message(msg[QStringLiteral("message")].toString()) 0111 , body(msg[DAP_BODY]) 0112 , errorBody(success ? std::nullopt : parseOptionalObject<Message>(body.toObject()[QStringLiteral("error")])) 0113 { 0114 } 0115 0116 bool Response::isCancelled() const 0117 { 0118 return message == QStringLiteral("cancelled"); 0119 } 0120 0121 ProcessInfo::ProcessInfo(const QJsonObject &body) 0122 : name(body[DAP_NAME].toString()) 0123 , systemProcessId(parseOptionalInt(body[DAP_SYSTEM_PROCESS_ID])) 0124 , isLocalProcess(parseOptionalBool(body[DAP_IS_LOCAL_PROCESS])) 0125 , startMethod(parseOptionalString(body[DAP_START_METHOD])) 0126 , pointerSize(parseOptionalInt(body[DAP_POINTER_SIZE])) 0127 { 0128 } 0129 0130 Output::Output(const QJsonObject &body) 0131 : category(Category::Unknown) 0132 , output(body[DAP_OUTPUT].toString()) 0133 , group(std::nullopt) 0134 , variablesReference(parseOptionalInt(body[DAP_VARIABLES_REFERENCE])) 0135 , source(parseOptionalObject<Source>(DAP_SOURCE)) 0136 , line(parseOptionalInt(body[DAP_LINE])) 0137 , column(parseOptionalInt(body[DAP_COLUMN])) 0138 , data(body[DAP_DATA]) 0139 { 0140 if (body.contains(DAP_GROUP)) { 0141 const auto value = body[DAP_GROUP].toString(); 0142 if (DAP_START == value) { 0143 group = Group::Start; 0144 } else if (QStringLiteral("startCollapsed") == value) { 0145 group = Group::StartCollapsed; 0146 } else if (QStringLiteral("end") == value) { 0147 group = Group::End; 0148 } 0149 } 0150 if (body.contains(DAP_CATEGORY)) { 0151 const auto value = body[DAP_CATEGORY].toString(); 0152 if (QStringLiteral("console") == value) { 0153 category = Category::Console; 0154 } else if (QStringLiteral("important") == value) { 0155 category = Category::Important; 0156 } else if (QStringLiteral("stdout") == value) { 0157 category = Category::Stdout; 0158 } else if (QStringLiteral("stderr") == value) { 0159 category = Category::Stderr; 0160 } else if (QStringLiteral("telemetry") == value) { 0161 category = Category::Telemetry; 0162 } 0163 } 0164 } 0165 0166 Output::Output(const QString &output, const Output::Category &category) 0167 : category(category) 0168 , output(output) 0169 { 0170 } 0171 0172 bool Output::isSpecialOutput() const 0173 { 0174 return (category != Category::Stderr) && (category != Category::Stdout); 0175 } 0176 0177 QString Source::unifiedId() const 0178 { 0179 return getUnifiedId(path, sourceReference); 0180 } 0181 0182 QString Source::getUnifiedId(const QString &path, std::optional<int> sourceReference) 0183 { 0184 if (sourceReference.value_or(0) > 0) { 0185 return QString::number(*sourceReference); 0186 } 0187 return path; 0188 } 0189 0190 Source::Source(const QJsonObject &body) 0191 : name(body[DAP_NAME].toString()) 0192 , path(body[DAP_PATH].toString()) 0193 , sourceReference(parseOptionalInt(body[DAP_SOURCE_REFERENCE])) 0194 , presentationHint(parseOptionalString(body[DAP_PRESENTATION_HINT])) 0195 , origin(body[DAP_ORIGIN].toString()) 0196 , adapterData(body[DAP_ADAPTER_DATA]) 0197 { 0198 // sources 0199 if (body.contains(DAP_SOURCES)) { 0200 const auto values = body[DAP_SOURCES].toArray(); 0201 for (const auto &item : values) { 0202 sources << Source(item.toObject()); 0203 } 0204 } 0205 0206 // checksums 0207 if (body.contains(DAP_CHECKSUMS)) { 0208 const auto values = body[DAP_CHECKSUMS].toArray(); 0209 for (const auto &item : values) { 0210 checksums << Checksum(item.toObject()); 0211 } 0212 } 0213 } 0214 0215 Source::Source(const QString &path) 0216 : path(path) 0217 { 0218 } 0219 0220 QJsonObject Source::toJson() const 0221 { 0222 QJsonObject out; 0223 if (!name.isEmpty()) { 0224 out[DAP_NAME] = name; 0225 } 0226 if (!path.isEmpty()) { 0227 out[DAP_PATH] = path; 0228 } 0229 if (sourceReference) { 0230 out[DAP_SOURCE_REFERENCE] = *sourceReference; 0231 } 0232 if (presentationHint) { 0233 out[DAP_PRESENTATION_HINT] = *presentationHint; 0234 } 0235 if (!origin.isEmpty()) { 0236 out[DAP_ORIGIN] = origin; 0237 } 0238 if (!adapterData.isNull() && !adapterData.isUndefined()) { 0239 out[DAP_ADAPTER_DATA] = adapterData; 0240 } 0241 if (!sources.isEmpty()) { 0242 out[DAP_SOURCES] = toJsonArray(sources); 0243 } 0244 if (!checksums.isEmpty()) { 0245 out[DAP_CHECKSUMS] = toJsonArray(checksums); 0246 } 0247 return out; 0248 } 0249 0250 Checksum::Checksum(const QJsonObject &body) 0251 : checksum(body[DAP_CHECKSUM].toString()) 0252 , algorithm(body[DAP_ALGORITHM].toString()) 0253 { 0254 } 0255 0256 QJsonObject Checksum::toJson() const 0257 { 0258 QJsonObject out; 0259 out[DAP_CHECKSUM] = checksum; 0260 out[DAP_ALGORITHM] = algorithm; 0261 return out; 0262 } 0263 0264 Capabilities::Capabilities(const QJsonObject &body) 0265 : supportsConfigurationDoneRequest(body[QStringLiteral("supportsConfigurationDoneRequest")].toBool()) 0266 , supportsFunctionBreakpoints(body[QStringLiteral("supportsFunctionBreakpoints")].toBool()) 0267 , supportsConditionalBreakpoints(body[QStringLiteral("supportsConditionalBreakpoints")].toBool()) 0268 , supportsHitConditionalBreakpoints(body[QStringLiteral("supportsHitConditionalBreakpoints")].toBool()) 0269 , supportsLogPoints(body[QStringLiteral("supportsLogPoints")].toBool()) 0270 , supportsModulesRequest(body[QStringLiteral("supportsModulesRequest")].toBool()) 0271 , supportsTerminateRequest(body[QStringLiteral("supportsTerminateRequest")].toBool()) 0272 , supportTerminateDebuggee(body[QStringLiteral("supportTerminateDebuggee")].toBool()) 0273 , supportsGotoTargetsRequest(body[QStringLiteral("supportsGotoTargetsRequest")].toBool()) 0274 { 0275 } 0276 0277 ThreadEvent::ThreadEvent(const QJsonObject &body) 0278 : reason(body[DAP_REASON].toString()) 0279 , threadId(body[DAP_THREAD_ID].toInt()) 0280 { 0281 } 0282 0283 StoppedEvent::StoppedEvent(const QJsonObject &body) 0284 : reason(body[DAP_REASON].toString()) 0285 , description(parseOptionalString(body[QStringLiteral("description")])) 0286 , threadId(body[DAP_THREAD_ID].toInt()) 0287 , preserveFocusHint(parseOptionalBool(body[QStringLiteral("preserveFocusHint")])) 0288 , text(parseOptionalString(body[QStringLiteral("text")])) 0289 , allThreadsStopped(parseOptionalBool(body[QStringLiteral("allThreadsStopped")])) 0290 , hitBreakpointsIds(parseOptionalIntList(body[QStringLiteral("hitBreakpointsIds")])) 0291 { 0292 } 0293 0294 Thread::Thread(const QJsonObject &body) 0295 : id(body[DAP_ID].toInt()) 0296 , name(body[DAP_NAME].toString()) 0297 { 0298 } 0299 0300 Thread::Thread(const int id) 0301 : id(id) 0302 , name(QString()) 0303 { 0304 } 0305 0306 QList<Thread> Thread::parseList(const QJsonArray &threads) 0307 { 0308 return parseObjectList<Thread>(threads); 0309 } 0310 0311 StackFrame::StackFrame(const QJsonObject &body) 0312 : id(body[DAP_ID].toInt()) 0313 , name(body[DAP_NAME].toString()) 0314 , source(parseOptionalObject<Source>(body[DAP_SOURCE])) 0315 , line(body[DAP_LINE].toInt()) 0316 , column(body[DAP_COLUMN].toInt()) 0317 , endLine(parseOptionalInt(body[QStringLiteral("endLine")])) 0318 , canRestart(parseOptionalBool((body[QStringLiteral("canRestart")]))) 0319 , instructionPointerReference(parseOptionalString(body[QStringLiteral("instructionPointerReference")])) 0320 , moduleId_int(parseOptionalInt(body[DAP_MODULE_ID])) 0321 , moduleId_str(parseOptionalString(body[DAP_MODULE_ID])) 0322 , presentationHint(parseOptionalString(body[DAP_PRESENTATION_HINT])) 0323 { 0324 } 0325 0326 StackTraceInfo::StackTraceInfo(const QJsonObject &body) 0327 : stackFrames(parseObjectList<StackFrame>(body[QStringLiteral("stackFrames")].toArray())) 0328 , totalFrames(parseOptionalInt(body[QStringLiteral("totalFrames")])) 0329 { 0330 } 0331 0332 Module::Module(const QJsonObject &body) 0333 : id_int(parseOptionalInt(body[DAP_ID])) 0334 , id_str(parseOptionalString(body[DAP_ID])) 0335 , name(body[DAP_NAME].toString()) 0336 , path(parseOptionalString(body[DAP_PATH])) 0337 , isOptimized(parseOptionalBool(body[QStringLiteral("isOptimized")])) 0338 , isUserCode(parseOptionalBool(body[QStringLiteral("isUserCode")])) 0339 , version(parseOptionalString(body[QStringLiteral("version")])) 0340 , symbolStatus(parseOptionalString(body[QStringLiteral("symbolStatus")])) 0341 , symbolFilePath(parseOptionalString(body[QStringLiteral("symbolFilePath")])) 0342 , dateTimeStamp(parseOptionalString(body[QStringLiteral("dateTimeStamp")])) 0343 , addressRange(parseOptionalString(body[QStringLiteral("addressRange")])) 0344 { 0345 } 0346 0347 ModuleEvent::ModuleEvent(const QJsonObject &body) 0348 : reason(body[DAP_REASON].toString()) 0349 , module(Module(body[QStringLiteral("module")].toObject())) 0350 { 0351 } 0352 0353 Scope::Scope(const QJsonObject &body) 0354 : name(body[DAP_NAME].toString()) 0355 , presentationHint(parseOptionalString(body[DAP_PRESENTATION_HINT])) 0356 , variablesReference(body[DAP_VARIABLES_REFERENCE].toInt()) 0357 , namedVariables(parseOptionalInt(body[QStringLiteral("namedVariables")])) 0358 , indexedVariables(parseOptionalInt(body[QStringLiteral("indexedVariables")])) 0359 , expensive(parseOptionalBool(body[QStringLiteral("expensive")])) 0360 , source(parseOptionalObject<Source>(body[QStringLiteral("source")])) 0361 , line(parseOptionalInt(body[QStringLiteral("line")])) 0362 , column(parseOptionalInt(body[QStringLiteral("column")])) 0363 , endLine(parseOptionalInt(body[QStringLiteral("endLine")])) 0364 , endColumn(parseOptionalInt(body[QStringLiteral("endColumn")])) 0365 { 0366 } 0367 0368 Scope::Scope(int variablesReference, QString name) 0369 : name(name) 0370 , variablesReference(variablesReference) 0371 { 0372 } 0373 0374 QList<Scope> Scope::parseList(const QJsonArray &scopes) 0375 { 0376 return parseObjectList<Scope>(scopes); 0377 } 0378 0379 Variable::Variable(const QJsonObject &body) 0380 : name(body[DAP_NAME].toString()) 0381 , value(body[QStringLiteral("value")].toString()) 0382 , type(parseOptionalString(body[DAP_TYPE].toString())) 0383 , evaluateName(parseOptionalString(body[QStringLiteral("evaluateName")].toString())) 0384 , variablesReference(body[DAP_VARIABLES_REFERENCE].toInt()) 0385 , namedVariables(parseOptionalInt(body[QStringLiteral("namedVariables")])) 0386 , indexedVariables(parseOptionalInt(body[QStringLiteral("indexedVariables")])) 0387 , memoryReference(parseOptionalString(body[QStringLiteral("memoryReference")])) 0388 { 0389 } 0390 0391 Variable::Variable(const QString &name, const QString &value, const int reference) 0392 : name(name) 0393 , value(value) 0394 , variablesReference(reference) 0395 { 0396 } 0397 0398 QList<Variable> Variable::parseList(const QJsonArray &variables) 0399 { 0400 return parseObjectList<Variable>(variables); 0401 } 0402 0403 ModulesInfo::ModulesInfo(const QJsonObject &body) 0404 : modules(parseObjectList<Module>(body[DAP_MODULES].toArray())) 0405 , totalModules(parseOptionalInt(body[QStringLiteral("totalModules")])) 0406 { 0407 } 0408 0409 ContinuedEvent::ContinuedEvent(const QJsonObject &body) 0410 : threadId(body[DAP_THREAD_ID].toInt()) 0411 , allThreadsContinued(parseOptionalBool(body[DAP_ALL_THREADS_CONTINUED])) 0412 { 0413 } 0414 0415 ContinuedEvent::ContinuedEvent(int threadId, bool allThreadsContinued) 0416 : threadId(threadId) 0417 , allThreadsContinued(allThreadsContinued) 0418 { 0419 } 0420 0421 SourceContent::SourceContent(const QJsonObject &body) 0422 : content(body[QStringLiteral("content")].toString()) 0423 , mimeType(parseOptionalString(body[QStringLiteral("mimeType")])) 0424 { 0425 } 0426 0427 SourceContent::SourceContent(const QString &path) 0428 { 0429 const QFileInfo file(path); 0430 if (file.isFile() && file.exists()) { 0431 QFile fd(path); 0432 if (fd.open(QIODevice::ReadOnly | QIODevice::Text)) { 0433 content = QString::fromLocal8Bit(fd.readAll()); 0434 } 0435 } 0436 } 0437 0438 SourceBreakpoint::SourceBreakpoint(const QJsonObject &body) 0439 : line(body[DAP_LINE].toInt()) 0440 , column(parseOptionalInt(body[DAP_COLUMN])) 0441 , condition(parseOptionalString(body[DAP_CONDITION])) 0442 , hitCondition(parseOptionalString(body[DAP_HIT_CONDITION])) 0443 , logMessage(parseOptionalString(body[QStringLiteral("logMessage")])) 0444 { 0445 } 0446 0447 SourceBreakpoint::SourceBreakpoint(const int line) 0448 : line(line) 0449 { 0450 } 0451 0452 QJsonObject SourceBreakpoint::toJson() const 0453 { 0454 QJsonObject out; 0455 out[DAP_LINE] = line; 0456 if (condition) { 0457 out[DAP_CONDITION] = *condition; 0458 } 0459 if (column) { 0460 out[DAP_COLUMN] = *column; 0461 } 0462 if (hitCondition) { 0463 out[DAP_HIT_CONDITION] = *hitCondition; 0464 } 0465 if (logMessage) { 0466 out[DAP_LOG_MESSAGE] = *logMessage; 0467 } 0468 return out; 0469 } 0470 0471 Breakpoint::Breakpoint(const QJsonObject &body) 0472 : id(parseOptionalInt(body[DAP_ID])) 0473 , verified(body[QStringLiteral("verified")].toBool()) 0474 , message(parseOptionalString(body[QStringLiteral("message")])) 0475 , source(parseOptionalObject<Source>(body[DAP_SOURCE])) 0476 , line(parseOptionalInt(body[DAP_LINE])) 0477 , column(parseOptionalInt(body[DAP_COLUMN])) 0478 , endLine(parseOptionalInt(body[DAP_END_LINE])) 0479 , endColumn(parseOptionalInt(body[DAP_END_COLUMN])) 0480 , instructionReference(parseOptionalString(body[QStringLiteral("instructionReference")])) 0481 , offset(parseOptionalInt(body[QStringLiteral("offset")])) 0482 { 0483 } 0484 0485 Breakpoint::Breakpoint(const int line) 0486 : line(line) 0487 { 0488 } 0489 0490 BreakpointEvent::BreakpointEvent(const QJsonObject &body) 0491 : reason(body[DAP_REASON].toString()) 0492 , breakpoint(Breakpoint(body[DAP_BREAKPOINT].toObject())) 0493 { 0494 } 0495 0496 EvaluateInfo::EvaluateInfo(const QJsonObject &body) 0497 : result(body[DAP_RESULT].toString()) 0498 , type(parseOptionalString(body[DAP_TYPE])) 0499 , variablesReference(body[DAP_VARIABLES_REFERENCE].toInt()) 0500 , namedVariables(parseOptionalInt(body[QStringLiteral("namedVariables")])) 0501 , indexedVariables(parseOptionalInt(body[QStringLiteral("indexedVariables")])) 0502 , memoryReference(parseOptionalString(body[QStringLiteral("memoryReference")])) 0503 { 0504 } 0505 0506 GotoTarget::GotoTarget(const QJsonObject &body) 0507 : id(body[DAP_ID].toInt()) 0508 , label(body[QStringLiteral("label")].toString()) 0509 , line(body[DAP_LINE].toInt()) 0510 , column(parseOptionalInt(body[DAP_COLUMN])) 0511 , endLine(parseOptionalInt(body[DAP_END_LINE])) 0512 , endColumn(parseOptionalInt(body[DAP_END_COLUMN])) 0513 , instructionPointerReference(parseOptionalString(body[QStringLiteral("instructionPointerReference")])) 0514 { 0515 } 0516 0517 QList<GotoTarget> GotoTarget::parseList(const QJsonArray &variables) 0518 { 0519 return parseObjectList<GotoTarget>(variables); 0520 } 0521 0522 } 0523 0524 #include "moc_entities.cpp"