File indexing completed on 2024-05-05 05:51:35
0001 // 0002 // Description: GDB variable parser 0003 // 0004 // This class parses a "flat" GDB variable string and outputs the structure of its child variables. 0005 // A signal is emitted for each of its child variable with a reference to its parent variable. 0006 // 0007 // Example : GDB gives the following flat value for the variable 'abcd': 0008 // "{name = \"hello\", d = {a = 0x7fffe0020ff0, size = 5}, value = 12}" 0009 // 0010 // The output will be the following structure : 0011 // 0012 // Symbol Value 0013 // ---------------------------------- 0014 // abcd 0015 // name "hello" 0016 // d 0017 // a 0x7fffe0020ff0 0018 // size 5 0019 // value 12 0020 // 0021 // SPDX-FileCopyrightText: 2010 Kåre Särs <kare.sars@iki.fi> 0022 // SPDX-FileCopyrightText: 2023 Rémi Peuchot <kde.remi@proton.me> 0023 // 0024 // SPDX-License-Identifier: LGPL-2.0-only 0025 0026 #include "gdbvariableparser.h" 0027 0028 // Smaller ids are reserved for scopes 0029 static constexpr int MIN_VAR_ID = 10; 0030 0031 GDBVariableParser::GDBVariableParser(QObject *parent) 0032 : QObject(parent) 0033 { 0034 } 0035 0036 void GDBVariableParser::insertVariable(const QString &name, const QString &value, const QString &type, bool changed) 0037 { 0038 QStringView tail(value); 0039 insertNamedVariable(0, name, 0, tail, type, changed); 0040 } 0041 0042 // Take the next available variable id, signal the variable and return its id 0043 int GDBVariableParser::createAndSignalVariable(int parentId, const QStringView name, const QStringView value, const QString &type, bool changed) 0044 { 0045 // Increment variable id, restart from MIN_VAR_ID in case of int overflow 0046 m_variableId = std::max(MIN_VAR_ID, m_variableId + 1); 0047 0048 dap::Variable var(name.toString(), value.toString(), m_variableId); 0049 var.valueChanged = changed; 0050 if (!type.isEmpty()) { 0051 var.type = type; 0052 } 0053 Q_EMIT variable(parentId, var); 0054 return m_variableId; 0055 } 0056 0057 enum ParsingState { 0058 Normal, 0059 InQuotedString, 0060 InParenthesis, 0061 }; 0062 0063 // Return the first index of the given char outside of quoted strings and parenthesis 0064 // example : finding the first comma in the following string: 0065 // "aaa\"bbb,bbb\\"bbb,bbb\\"bbb\"aaa(ccc,ccc)aaa,aaa,aaa" 0066 // ^ 0067 // gives this one : ^ 0068 // 0069 // because the string is decomposed into : 0070 // - bbb : "in quoted string" 0071 // - ccc : "in parenthesis" 0072 // - aaa : normal (where the comma is searched for) 0073 qsizetype firstIndexOf(const QStringView tail, QChar ch) 0074 { 0075 const QChar QUOTE((short)'"'); 0076 const QChar BACKSLASH((short)'\\'); 0077 const QChar OPENING_PARENTHESIS((short)'('); 0078 const QChar CLOSING_PARENTHESIS((short)')'); 0079 QChar previous(0); 0080 ParsingState state = Normal; 0081 for (int i = 0; i < tail.length(); i++) { 0082 QChar current = tail[i]; 0083 if (state == Normal) { 0084 if (current == ch) { 0085 return i; 0086 } 0087 if (current == QUOTE) { 0088 state = InQuotedString; 0089 } else if (current == OPENING_PARENTHESIS) { 0090 state = InParenthesis; 0091 } 0092 } else if (state == InQuotedString) { 0093 if (current == QUOTE && previous != BACKSLASH) { 0094 state = Normal; 0095 } 0096 } else if (state == InParenthesis) { 0097 if (current == CLOSING_PARENTHESIS) { 0098 state = Normal; 0099 } 0100 } 0101 previous = current; 0102 } 0103 return -1; 0104 } 0105 0106 // Return index of first char among 'characters' in tail 0107 qsizetype firstIndexOf(const QStringView tail, QString characters) 0108 { 0109 qsizetype first = -1; 0110 for (auto ch : characters) { 0111 qsizetype i = firstIndexOf(tail, ch); 0112 if (i != -1 && (first == -1 || i < first)) { 0113 first = i; 0114 } 0115 } 0116 return first; 0117 } 0118 0119 // If tail starts with pattern "name = value" : 0120 // - advance tail to first char of value 0121 // - extract and return name 0122 // else : 0123 // - let tail unchanged 0124 // - return empty string 0125 QStringView findVariableName(QStringView &tail) 0126 { 0127 const QChar EQUAL((short)'='); 0128 auto closingIndex = firstIndexOf(tail, QStringLiteral("=,{}")); 0129 if (closingIndex != -1 && tail[closingIndex] == EQUAL) { 0130 QStringView name = tail.mid(0, closingIndex).trimmed(); 0131 tail = tail.mid(closingIndex + 1).trimmed(); 0132 return name; 0133 } 0134 return QStringView(); 0135 } 0136 0137 // Parse the (eventually named) variable at the beginning of the given tail. 0138 // The pattern defines the value type : 0139 // - "name = value" : it's a named variable, 'itemIndex' is ignored 0140 // - "value" : it's an array item of index 'itemIndex' 0141 // The given tail will be advanced to the next character after this variable value. 0142 void GDBVariableParser::insertVariable(int parentId, int itemIndex, QStringView &tail, const QString &type, bool changed) 0143 { 0144 QStringView name = findVariableName(tail); 0145 if (name.isEmpty()) { 0146 // No variable name : it's an array item 0147 QString itemName = QStringLiteral("[%1]").arg(itemIndex); 0148 insertNamedVariable(parentId, itemName, itemIndex, tail, type, changed); 0149 return; 0150 } 0151 insertNamedVariable(parentId, name, itemIndex, tail, type, changed); 0152 } 0153 0154 // Parse the variable value at the beginning of the given tail. 0155 // The pattern defines the value type : 0156 // - if value contains an open brace, it's a parent object with children : "optional_string{child0, child1, ...}" 0157 // - else, it's a simple variable value 0158 // The given tail will be advanced to the next character after this variable value. 0159 void GDBVariableParser::insertNamedVariable(int parentId, QStringView name, int itemIndex, QStringView &tail, const QString &type, bool changed) 0160 { 0161 // Find an opening brace in the value (before next comma or next closing brace) 0162 const QChar OPENING_BRACE((short)'{'); 0163 int openingIndex = firstIndexOf(tail, QStringLiteral(",{}")); 0164 if (openingIndex != -1 && tail[openingIndex] == OPENING_BRACE) { 0165 // It's a parent object 0166 QString value = tail.mid(0, openingIndex).toString(); 0167 tail = tail.mid(openingIndex + 1).trimmed(); // Advance after the opening brace 0168 0169 if (tail.startsWith(QStringLiteral("}"))) { 0170 // It's an empty object 0171 value = value + QStringLiteral("{}"); 0172 createAndSignalVariable(parentId, name, value, type, changed); 0173 } else { 0174 // It contains some child variables 0175 value = value + QStringLiteral("{...}"); 0176 0177 // Create the parent object 0178 int id = createAndSignalVariable(parentId, name, value, type, changed); 0179 0180 // Insert the first child variable, siblings will be created recursively 0181 insertVariable(id, 0, tail, QStringLiteral(""), changed); 0182 } 0183 0184 // All child variables have been parsed, the parent is supposed to be closed now 0185 if (tail.startsWith(QStringLiteral("}"))) { 0186 tail = tail.mid(1).trimmed(); // Advance after the closing brace 0187 } else { 0188 qWarning() << "Missing closing brace at the end of parent variable"; 0189 } 0190 } else { 0191 // It's a simple variable value 0192 auto valueLength = firstIndexOf(tail, QStringLiteral(",}")); 0193 if (valueLength == -1) { 0194 // It's the last value in the tail : take everything else 0195 valueLength = tail.length(); 0196 } 0197 0198 // Extract the value 0199 QStringView value = tail.mid(0, valueLength).trimmed(); 0200 createAndSignalVariable(parentId, name, value, QStringLiteral(""), changed); 0201 tail = tail.mid(valueLength).trimmed(); // Advance after value 0202 } 0203 0204 // Variable has been parsed, eventually parse its next sibling 0205 if (tail.startsWith(QStringLiteral(","))) { 0206 // There is a sibling 0207 tail = tail.mid(1).trimmed(); // Advance after the comma 0208 insertVariable(parentId, itemIndex + 1, tail, QStringLiteral(""), changed); 0209 } 0210 } 0211 0212 #include "moc_gdbvariableparser.cpp"