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"