File indexing completed on 2024-04-28 04:38:36
0001 /* 0002 SPDX-FileCopyrightText: 2009 Vladimir Prus <ghost@cs.msu.su> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "mivariable.h" 0008 0009 #include "debuglog.h" 0010 #include "midebugsession.h" 0011 #include "mi/micommand.h" 0012 #include "stringhelpers.h" 0013 0014 #include <debugger/interfaces/ivariablecontroller.h> 0015 #include <interfaces/icore.h> 0016 0017 using namespace KDevelop; 0018 using namespace KDevMI; 0019 using namespace KDevMI::MI; 0020 0021 bool MIVariable::sessionIsAlive() const 0022 { 0023 if (!m_debugSession) 0024 return false; 0025 0026 IDebugSession::DebuggerState s = m_debugSession->state(); 0027 return s != IDebugSession::NotStartedState 0028 && s != IDebugSession::EndedState 0029 && !m_debugSession->debuggerStateIsOn(s_shuttingDown); 0030 } 0031 0032 MIVariable::MIVariable(MIDebugSession *session, TreeModel* model, TreeItem* parent, 0033 const QString& expression, const QString& display) 0034 : Variable(model, parent, expression, display) 0035 , m_debugSession(session) 0036 { 0037 } 0038 0039 MIVariable *MIVariable::createChild(const Value& child) 0040 { 0041 if (!m_debugSession) return nullptr; 0042 auto var = static_cast<MIVariable*>(m_debugSession->variableController()->createVariable(model(), this, child[QStringLiteral("exp")].literal())); 0043 var->setTopLevel(false); 0044 var->setVarobj(child[QStringLiteral("name")].literal()); 0045 bool hasMore = child[QStringLiteral("numchild")].toInt() != 0 || ( child.hasField(QStringLiteral("dynamic")) && child[QStringLiteral("dynamic")].toInt()!=0 ); 0046 var->setHasMoreInitial(hasMore); 0047 0048 // *this must be parent's child before we can set type and value 0049 appendChild(var); 0050 0051 var->setType(child[QStringLiteral("type")].literal()); 0052 var->setValue(formatValue(child[QStringLiteral("value")].literal())); 0053 var->setChanged(true); 0054 return var; 0055 } 0056 0057 MIVariable::~MIVariable() 0058 { 0059 if (!m_varobj.isEmpty()) 0060 { 0061 // Delete only top-level variable objects. 0062 if (topLevel()) { 0063 if (sessionIsAlive()) { 0064 m_debugSession->addCommand(VarDelete, QStringLiteral("\"%1\"").arg(m_varobj)); 0065 } 0066 } 0067 if (m_debugSession) 0068 m_debugSession->variableMapping().remove(m_varobj); 0069 } 0070 } 0071 0072 void MIVariable::setVarobj(const QString& v) 0073 { 0074 if (!m_debugSession) { 0075 qCWarning(DEBUGGERCOMMON) << "MIVariable::setVarobj called when its session died"; 0076 return; 0077 } 0078 if (!m_varobj.isEmpty()) { 0079 // this should not happen 0080 // but apparently it does when attachMaybe is called a second time before 0081 // the first -var-create call returned 0082 m_debugSession->variableMapping().remove(m_varobj); 0083 } 0084 m_varobj = v; 0085 m_debugSession->variableMapping()[m_varobj] = this; 0086 } 0087 0088 0089 static int nextId = 0; 0090 0091 class CreateVarobjHandler : public MICommandHandler 0092 { 0093 public: 0094 CreateVarobjHandler(MIVariable *variable, QObject *callback, const char *callbackMethod) 0095 : m_variable(variable), m_callback(callback), m_callbackMethod(callbackMethod) 0096 {} 0097 0098 void handle(const ResultRecord &r) override 0099 { 0100 if (!m_variable) return; 0101 bool hasValue = false; 0102 MIVariable* variable = m_variable.data(); 0103 variable->deleteChildren(); 0104 variable->setInScope(true); 0105 if (r.reason == QLatin1String("error")) { 0106 variable->setShowError(true); 0107 } else { 0108 variable->setVarobj(r[QStringLiteral("name")].literal()); 0109 0110 bool hasMore = false; 0111 if (r.hasField(QStringLiteral("has_more")) && r[QStringLiteral("has_more")].toInt()) 0112 // GDB swears there are more children. Trust it 0113 hasMore = true; 0114 else 0115 // There are no more children in addition to what 0116 // numchild reports. But, in KDevelop, the variable 0117 // is not yet expanded, and those numchild are not 0118 // fetched yet. So, if numchild != 0, hasMore should 0119 // be true. 0120 hasMore = r[QStringLiteral("numchild")].toInt() != 0; 0121 0122 variable->setHasMore(hasMore); 0123 0124 variable->setType(r[QStringLiteral("type")].literal()); 0125 variable->setValue(variable->formatValue(r[QStringLiteral("value")].literal())); 0126 hasValue = !r[QStringLiteral("value")].literal().isEmpty(); 0127 if (variable->isExpanded() && r[QStringLiteral("numchild")].toInt()) { 0128 variable->fetchMoreChildren(); 0129 } 0130 0131 if (variable->format() != KDevelop::Variable::Natural) { 0132 //TODO doesn't work for children as they are not yet loaded 0133 variable->formatChanged(); 0134 } 0135 } 0136 0137 if (m_callback && m_callbackMethod) { 0138 QMetaObject::invokeMethod(m_callback, m_callbackMethod, Q_ARG(bool, hasValue)); 0139 } 0140 } 0141 bool handlesError() override { return true; } 0142 0143 private: 0144 QPointer<MIVariable> m_variable; 0145 QObject *m_callback; 0146 const char *m_callbackMethod; 0147 }; 0148 0149 void MIVariable::attachMaybe(QObject *callback, const char *callbackMethod) 0150 { 0151 if (!m_varobj.isEmpty()) 0152 return; 0153 0154 // Try find a current session and attach to it 0155 if (!ICore::self()->debugController()) return; //happens on shutdown 0156 m_debugSession = static_cast<MIDebugSession*>(ICore::self()->debugController()->currentSession()); 0157 0158 if (sessionIsAlive()) { 0159 m_debugSession->addCommand(VarCreate, 0160 QStringLiteral("var%1 @ %2").arg(nextId++).arg(enquotedExpression()), 0161 new CreateVarobjHandler(this, callback, callbackMethod)); 0162 } 0163 } 0164 0165 void MIVariable::markAsDead() 0166 { 0167 m_varobj.clear(); 0168 } 0169 0170 class FetchMoreChildrenHandler : public MICommandHandler 0171 { 0172 public: 0173 FetchMoreChildrenHandler(MIVariable *variable, MIDebugSession *session) 0174 : m_variable(variable), m_session(session), m_activeCommands(1) 0175 {} 0176 0177 void handle(const ResultRecord &r) override 0178 { 0179 if (!m_variable) return; 0180 --m_activeCommands; 0181 0182 MIVariable* variable = m_variable.data(); 0183 0184 if (r.hasField(QStringLiteral("children"))) 0185 { 0186 const Value& children = r[QStringLiteral("children")]; 0187 for (int i = 0; i < children.size(); ++i) { 0188 const Value& child = children[i]; 0189 const QString& exp = child[QStringLiteral("exp")].literal(); 0190 if (exp == QLatin1String("public") || exp == QLatin1String("protected") || exp == QLatin1String("private")) { 0191 ++m_activeCommands; 0192 m_session->addCommand(VarListChildren, 0193 QStringLiteral("--all-values \"%1\"").arg(child[QStringLiteral("name")].literal()), 0194 this/*use again as handler*/); 0195 } else { 0196 variable->createChild(child); 0197 // it's automatically appended to variable's children list 0198 } 0199 } 0200 } 0201 0202 /* Note that we don't set hasMore to true if there are still active 0203 commands. The reason is that we don't want the user to have 0204 even theoretical ability to click on "..." item and confuse 0205 us. */ 0206 bool hasMore = false; 0207 if (r.hasField(QStringLiteral("has_more"))) 0208 hasMore = r[QStringLiteral("has_more")].toInt(); 0209 0210 variable->setHasMore(hasMore); 0211 if (m_activeCommands == 0) { 0212 variable->emitAllChildrenFetched(); 0213 delete this; 0214 } 0215 } 0216 bool handlesError() override { 0217 // FIXME: handle error? 0218 return false; 0219 } 0220 bool autoDelete() override { 0221 // we delete ourselves 0222 return false; 0223 } 0224 0225 private: 0226 QPointer<MIVariable> m_variable; 0227 MIDebugSession *m_session; 0228 int m_activeCommands; 0229 }; 0230 0231 void MIVariable::fetchMoreChildren() 0232 { 0233 int c = childItems.size(); 0234 // FIXME: should not even try this if app is not started. 0235 // Probably need to disable open, or something 0236 if (sessionIsAlive()) { 0237 m_debugSession->addCommand(VarListChildren, 0238 QStringLiteral("--all-values \"%1\" %2 %3") 0239 // fetch from .. to .. 0240 .arg(m_varobj).arg(c).arg(c + s_fetchStep), 0241 new FetchMoreChildrenHandler(this, m_debugSession)); 0242 } 0243 } 0244 0245 void MIVariable::handleUpdate(const Value& var) 0246 { 0247 if (var.hasField(QStringLiteral("type_changed")) 0248 && var[QStringLiteral("type_changed")].literal() == QLatin1String("true")) 0249 { 0250 deleteChildren(); 0251 // FIXME: verify that this check is right. 0252 setHasMore(var[QStringLiteral("new_num_children")].toInt() != 0); 0253 fetchMoreChildren(); 0254 } 0255 0256 if (var.hasField(QStringLiteral("in_scope")) && var[QStringLiteral("in_scope")].literal() == QLatin1String("false")) 0257 { 0258 setInScope(false); 0259 } 0260 else 0261 { 0262 setInScope(true); 0263 0264 if (var.hasField(QStringLiteral("new_num_children"))) { 0265 int nc = var[QStringLiteral("new_num_children")].toInt(); 0266 Q_ASSERT(nc != -1); 0267 setHasMore(false); 0268 while (childCount() > nc) { 0269 TreeItem *c = child(childCount()-1); 0270 removeChild(childCount()-1); 0271 delete c; 0272 } 0273 } 0274 0275 if (var.hasField(QStringLiteral("new_children"))) 0276 { 0277 const Value& children = var[QStringLiteral("new_children")]; 0278 if (m_debugSession) { 0279 for (int i = 0; i < children.size(); ++i) { 0280 createChild(children[i]); 0281 // it's automatically appended to this's children list 0282 } 0283 } 0284 } 0285 0286 if (var.hasField(QStringLiteral("type_changed")) && var[QStringLiteral("type_changed")].literal() == QLatin1String("true")) { 0287 setType(var[QStringLiteral("new_type")].literal()); 0288 } 0289 setValue(formatValue(var[QStringLiteral("value")].literal())); 0290 setChanged(true); 0291 setHasMore(var.hasField(QStringLiteral("has_more")) && var[QStringLiteral("has_more")].toInt()); 0292 } 0293 } 0294 0295 const QString& MIVariable::varobj() const 0296 { 0297 return m_varobj; 0298 } 0299 0300 QString MIVariable::enquotedExpression() const 0301 { 0302 return Utils::quoteExpression(expression()); 0303 } 0304 0305 0306 class SetFormatHandler : public MICommandHandler 0307 { 0308 public: 0309 explicit SetFormatHandler(MIVariable *var) 0310 : m_variable(var) 0311 {} 0312 0313 void handle(const ResultRecord &r) override 0314 { 0315 if(m_variable && r.hasField(QStringLiteral("value"))) 0316 m_variable->setValue(m_variable->formatValue(r[QStringLiteral("value")].literal())); 0317 } 0318 private: 0319 QPointer<MIVariable> m_variable; 0320 }; 0321 0322 void MIVariable::formatChanged() 0323 { 0324 if(childCount()) 0325 { 0326 for (TreeItem* item : qAsConst(childItems)) { 0327 Q_ASSERT(dynamic_cast<MIVariable*>(item)); 0328 if (auto* var = qobject_cast<MIVariable*>(item)) { 0329 var->setFormat(format()); 0330 } 0331 } 0332 } 0333 else 0334 { 0335 if (sessionIsAlive()) { 0336 m_debugSession->addCommand(VarSetFormat, 0337 QStringLiteral(" %1 %2 ").arg(m_varobj, format2str(format())), 0338 new SetFormatHandler(this)); 0339 } 0340 } 0341 } 0342 0343 QString MIVariable::formatValue(const QString &rawValue) const 0344 { 0345 return rawValue; 0346 } 0347 0348 #include "moc_mivariable.cpp"