File indexing completed on 2021-12-21 13:15:51

0001 /*
0002  * XDebug-specific Variable
0003  *
0004  * Copyright 2009 Vladimir Prus <ghost@cs.msu.su>
0005  * Copyright 2009 Niko Sams <niko.sams@gmail.com>
0006  *
0007  * This program is free software; you can redistribute it and/or modify
0008  * it under the terms of the GNU General Public License as
0009  * published by the Free Software Foundation; either version 2 of the
0010  * License, or (at your option) any later version.
0011  *
0012  * This program is distributed in the hope that it will be useful,
0013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0015  * GNU General Public License for more details.
0016  *
0017  * You should have received a copy of the GNU General Public
0018  * License along with this program; if not, write to the
0019  * Free Software Foundation, Inc.,
0020  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
0021  */
0022 
0023 #include "variable.h"
0024 #include "debugsession.h"
0025 
0026 #include <QDebug>
0027 
0028 #include <QDomElement>
0029 #include <QXmlStreamReader>
0030 
0031 #include <interfaces/icore.h>
0032 #include <debugger/interfaces/ivariablecontroller.h>
0033 #include <debugger/interfaces/iframestackmodel.h>
0034 
0035 #include "connection.h"
0036 #include "debuggerdebug.h"
0037 
0038 namespace XDebug {
0039 static bool hasStartedSession()
0040 {
0041     KDevelop::IDebugSession* session = KDevelop::ICore::self()->debugController()->currentSession();
0042     if (!session) {
0043         return false;
0044     }
0045 
0046     KDevelop::IDebugSession::DebuggerState s = session->state();
0047     return s != KDevelop::IDebugSession::NotStartedState
0048            && s != KDevelop::IDebugSession::EndedState;
0049 }
0050 
0051 Variable::Variable(KDevelop::TreeModel* model, KDevelop::TreeItem* parent,
0052                    const QString& expression, const QString& display)
0053     : KDevelop::Variable(model, parent, expression, display)
0054 {
0055 }
0056 
0057 Variable::~Variable()
0058 {
0059 }
0060 
0061 class PropertyGetCallback
0062     : public CallbackBase
0063 {
0064 public:
0065     PropertyGetCallback(Variable* variable, QObject* callback, const char* callbackMethod)
0066         : m_variable(variable)
0067         , m_callback(callback)
0068         , m_callbackMethod(callbackMethod)
0069     {}
0070 
0071     void execute(const QDomDocument& xml) override
0072     {
0073         qCDebug(KDEV_PHP_DEBUGGER) << xml.toString();
0074         Q_ASSERT(xml.documentElement().attribute("command") == "property_get");
0075 
0076         if (!m_variable) {return;}
0077 
0078         bool hasValue = false;
0079         QDomElement el = xml.documentElement().firstChildElement();
0080         if (el.nodeName() == "error") {
0081             qCDebug(KDEV_PHP_DEBUGGER) << el.firstChildElement().text();
0082             //hasValue=false
0083         } else {
0084             el = xml.documentElement().firstChildElement("property");
0085             hasValue = !el.isNull();
0086             if (hasValue) {
0087                 m_variable->handleProperty(el);
0088             }
0089         }
0090 
0091         if (m_callback && m_callbackMethod) {
0092             QMetaObject::invokeMethod(m_callback, m_callbackMethod, Q_ARG(bool, hasValue));
0093         }
0094     }
0095 
0096     bool allowError() const override { return true; }
0097 
0098 private:
0099     QPointer<Variable> m_variable;
0100     QObject* m_callback;
0101     const char* m_callbackMethod;
0102 };
0103 
0104 void Variable::attachMaybe(QObject* callback, const char* callbackMethod)
0105 {
0106     if (hasStartedSession()) {
0107         // FIXME: Eventually, should be a property of variable.
0108         KDevelop::IDebugSession* is = KDevelop::ICore::self()->debugController()->currentSession();
0109         DebugSession* s = static_cast<DebugSession*>(is);
0110         QStringList args;
0111         args << "-n " + expression();
0112         args << QString("-d %0").arg(s->frameStackModel()->currentFrame());
0113         s->connection()->sendCommand("property_get", args, QByteArray(),
0114                                      new PropertyGetCallback(this, callback, callbackMethod));
0115     }
0116 }
0117 
0118 void Variable::fetchMoreChildren()
0119 {
0120     // FIXME: should not even try this if app is not started.
0121     // Probably need to disable open, or something
0122     if (hasStartedSession()) {
0123         // FIXME: Eventually, should be a property of variable.
0124         KDevelop::IDebugSession* is = KDevelop::ICore::self()->debugController()->currentSession();
0125         DebugSession* s = static_cast<DebugSession*>(is);
0126         qCDebug(KDEV_PHP_DEBUGGER) << expression() << m_fullName;
0127         QStringList args;
0128         args << "-n " + m_fullName;
0129         args << QString("-d %0").arg(s->frameStackModel()->currentFrame());
0130         s->connection()->sendCommand("property_get", args, QByteArray(),
0131                                      new PropertyGetCallback(this, nullptr, nullptr));
0132     }
0133 }
0134 
0135 void Variable::handleProperty(const QDomElement& xml)
0136 {
0137     Q_ASSERT(!xml.isNull());
0138     Q_ASSERT(xml.nodeName() == "property");
0139 
0140     setInScope(true);
0141 
0142     m_fullName = xml.attribute("fullname");
0143     //qCDebug(KDEV_PHP_DEBUGGER) << m_fullName;
0144     if (xml.firstChild().isText()) {
0145         QString v  = xml.firstChild().toText().data();
0146         if (xml.attribute("encoding") == "base64") {
0147             //TODO: use Connection::m_codec->toUnicode
0148             v = QString::fromUtf8(QByteArray::fromBase64(xml.text().toUtf8()));
0149         }
0150         //qCDebug(KDEV_PHP_DEBUGGER) << "value" << v;
0151         setValue(v);
0152     }
0153 
0154     QMap<QString, Variable*> existing;
0155     for (int i = 0; i < childCount() - (hasMore() ? 1 : 0); i++) {
0156         Q_ASSERT(dynamic_cast<Variable*>(child(i)));
0157         Variable* v = static_cast<Variable*>(child(i));
0158         existing[v->expression()] = v;
0159     }
0160 
0161     QSet<QString> current;
0162     QDomElement el = xml.firstChildElement("property");
0163     while (!el.isNull()) {
0164         QString name = el.attribute("name");
0165         //qCDebug(KDEV_PHP_DEBUGGER) << name;
0166         current << name;
0167         Variable* v = nullptr;
0168         if (!existing.contains(name)) {
0169             v = new Variable(model(), this, name);
0170             appendChild(v, false);
0171         } else {
0172             v = existing[name];
0173         }
0174 
0175         v->handleProperty(el);
0176 
0177         el = el.nextSiblingElement("property");
0178     }
0179 
0180     for (int i = 0; i < childCount() - (hasMore() ? 1 : 0); ++i) {
0181         Q_ASSERT(dynamic_cast<KDevelop::Variable*>(child(i)));
0182         KDevelop::Variable* v = static_cast<KDevelop::Variable*>(child(i));
0183         if (!current.contains(v->expression())) {
0184             removeChild(i);
0185             --i;
0186             delete v;
0187         }
0188     }
0189 
0190     if (!childCount() && xml.attribute("children") == "1") {
0191         qCDebug(KDEV_PHP_DEBUGGER) << "has more" << this;
0192         setHasMore(true);
0193         if (isExpanded()) {
0194             fetchMoreChildren();
0195         }
0196     }
0197 }
0198 
0199 QString Variable::fullName() const
0200 {
0201     return m_fullName;
0202 }
0203 }