OSDN Git Service

Debugger[New CDDB]: Dump strings and simple Qt types.
authorFriedemann Kleint <Friedemann.Kleint@nokia.com>
Fri, 26 Nov 2010 14:51:56 +0000 (15:51 +0100)
committerFriedemann Kleint <Friedemann.Kleint@nokia.com>
Fri, 26 Nov 2010 14:51:56 +0000 (15:51 +0100)
Add infrastructure for simple dumpers in Symbol group.
Fix display of class values.

src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp
src/libs/qtcreatorcdbext/symbolgroup.cpp
src/libs/qtcreatorcdbext/symbolgroup.h
src/libs/qtcreatorcdbext/symbolgroupvalue.cpp
src/libs/qtcreatorcdbext/symbolgroupvalue.h
tests/manual/gdbdebugger/gui/mainwindow.cpp
tests/manual/gdbdebugger/gui/mainwindow.h
tests/manual/gdbdebugger/gui/mainwindow.ui

index 53bef0a..32ed013 100644 (file)
@@ -204,11 +204,14 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
         symGroup->expandList(expandedInames, errorMessage);
     if (!uninitializedInames.empty())
         symGroup->markUninitialized(uninitializedInames);
-    // Complete dump
-    if (iname.empty())
-        return debugOutput ? symGroup->debug(debugOutput - 1) : symGroup->dump(humanReadableGdbmi);
-    // Look up iname
-    return symGroup->dump(iname, humanReadableGdbmi, errorMessage);
+
+    if (debugOutput)
+        return symGroup->debug(iname, debugOutput - 1);
+
+    const SymbolGroupValueContext dumpContext(exc.dataSpaces());
+    return iname.empty() ?
+           symGroup->dump(dumpContext, humanReadableGdbmi) :
+           symGroup->dump(iname, dumpContext, humanReadableGdbmi, errorMessage);
 }
 
 extern "C" HRESULT CALLBACK locals(CIDebugClient *client, PCSTR args)
index 5c2a1e6..65334b4 100644 (file)
@@ -28,6 +28,7 @@
 **************************************************************************/
 
 #include "symbolgroup.h"
+#include "symbolgroupvalue.h"
 #include "stringutils.h"
 #include "base64.h"
 
@@ -443,10 +444,18 @@ static void fixValue(const std::string &type, std::wstring *value)
         value->erase(0, 2);
         return;
     }
+    // Fix long class names on std containers 'class std::tree<...>' -> 'class std::tree<>'
+    if (value->compare(0, 6, L"class ") == 0) {
+        const std::string::size_type openTemplate = value->find(L'<');
+        if (openTemplate != std::string::npos) {
+            value->erase(openTemplate + 1, value->size() - openTemplate - 2);
+            return;
+        }
+    }
 }
 
 // Check for ASCII-encode-able stuff. Plain characters + tabs at the most, no newline.
-static bool isSevenBitClean(const wchar_t *buf, ULONG size)
+static bool isSevenBitClean(const wchar_t *buf, size_t size)
 {
     const wchar_t *bufEnd = buf + size;
     for (const wchar_t *bufPtr = buf; bufPtr < bufEnd; bufPtr++) {
@@ -464,66 +473,72 @@ std::string SymbolGroupNode::type() const
     return SUCCEEDED(hr) ? std::string(buf) : std::string();
 }
 
-wchar_t *SymbolGroupNode::getValue(ULONG *obtainedSizeIn /* = 0 */) const
+ULONG64 SymbolGroupNode::address() const
+{
+    ULONG64 address = 0;
+    const HRESULT hr = m_symbolGroup->debugSymbolGroup()->GetSymbolOffset(m_index, &address);
+    if (SUCCEEDED(hr))
+        return address;
+    return 0;
+}
+
+std::wstring SymbolGroupNode::symbolGroupRawValue() const
 {
     // Determine size and return allocated buffer
-    if (obtainedSizeIn)
-        *obtainedSizeIn = 0;
     const ULONG maxValueSize = 262144;
     ULONG obtainedSize = 0;
     HRESULT hr = m_symbolGroup->debugSymbolGroup()->GetSymbolValueTextWide(m_index, NULL, maxValueSize, &obtainedSize);
     if (FAILED(hr))
-        return 0;
+        return std::wstring();
     if (obtainedSize > maxValueSize)
         obtainedSize = maxValueSize;
     wchar_t *buffer = new wchar_t[obtainedSize];
     hr = m_symbolGroup->debugSymbolGroup()->GetSymbolValueTextWide(m_index, buffer, obtainedSize, &obtainedSize);
-    if (FAILED(hr)) { // Whoops, should not happen
-        delete [] buffer;
-        return 0;
-    }
-    if (obtainedSizeIn)
-        *obtainedSizeIn = obtainedSize;
-    return buffer;
+    if (FAILED(hr)) // Whoops, should not happen
+        buffer[0] = 0;
+    const std::wstring rc(buffer);
+    delete [] buffer;
+    return rc;
 }
 
-ULONG64 SymbolGroupNode::address() const
+std::wstring SymbolGroupNode::symbolGroupFixedValue() const
 {
-    ULONG64 address = 0;
-    const HRESULT hr = m_symbolGroup->debugSymbolGroup()->GetSymbolOffset(m_index, &address);
-    if (SUCCEEDED(hr))
-        return address;
-    return 0;
+    std::wstring value = symbolGroupRawValue();
+    fixValue(type(), &value);
+    return value;
 }
 
-std::wstring SymbolGroupNode::rawValue() const
+// Value to be reported to debugger
+std::wstring SymbolGroupNode::displayValue(const SymbolGroupValueContext &ctx)
 {
-    std::wstring rc;
-    if (const wchar_t *wbuf = getValue()) {
-        rc = wbuf;
-        delete[] wbuf;
-    }
-    return rc;
+    if (m_flags & Uninitialized)
+        return L"<not in scope>";
+    if ((m_flags & DumperMask) == 0)
+        m_flags |= dumpSimpleType(this , ctx, &m_dumperValue);
+    if (m_flags & DumperOk)
+        return m_dumperValue;
+    return symbolGroupFixedValue();
 }
 
-std::wstring SymbolGroupNode::fixedValue() const
+SymbolGroupNode *SymbolGroupNode::childAt(unsigned i) const
 {
-    std::wstring value = rawValue();
-    fixValue(type(), &value);
-    return value;
+    return i < m_children.size() ? m_children.at(i) : static_cast<SymbolGroupNode *>(0);
 }
 
-SymbolGroupNode *SymbolGroupNode::childAt(unsigned i) const
+unsigned SymbolGroupNode::indexByIName(const char *n) const
 {
-    return i < m_children.size() ? m_children.at(i) : static_cast<SymbolGroupNode *>(0);
+    const VectorIndexType size = m_children.size();
+    for (VectorIndexType i = 0; i < size; i++)
+        if ( m_children.at(i)->iName() == n )
+            return unsigned(i);
+    return unsigned(-1);
 }
 
 SymbolGroupNode *SymbolGroupNode::childByIName(const char *n) const
 {
-    const SymbolGroupNodePtrVector::const_iterator childrenEnd = m_children.end();
-    for (SymbolGroupNodePtrVector::const_iterator it = m_children.begin(); it != childrenEnd; ++it)
-        if ( (*it)->iName() == n )
-            return *it;
+    const unsigned index = indexByIName(n);
+    if (index != unsigned(-1))
+        return m_children.at(index);
     return 0;
 }
 
@@ -533,51 +548,32 @@ static inline void indentStream(std::ostream &str, unsigned depth)
         str << "  ";
 }
 
-void SymbolGroupNode::dump(std::ostream &str, unsigned child, unsigned depth,
-                           bool humanReadable) const
+void SymbolGroupNode::dump(std::ostream &str, const SymbolGroupValueContext &ctx)
 {
     const std::string iname = fullIName();
     const std::string t = type();
 
-    if (child) { // Separate list of children
-        str << ',';
-        if (humanReadable)
-            str << '\n';
-    }
-
-    if (humanReadable)
-        indentStream(str, depth);
-    str << "{iname=\"" << iname << "\",exp=\"" << iname << "\",name=\"" << m_name
+    str << "iname=\"" << iname << "\",exp=\"" << iname << "\",name=\"" << m_name
         << "\",type=\"" << t << '"';
 
     if (const ULONG64 addr = address())
         str << ",addr=\"" << std::hex << std::showbase << addr << std::noshowbase << std::dec
                << '"';
 
-    bool valueEditable = true;
-    bool valueEnabled = true;
-
     const bool uninitialized = m_flags & Uninitialized;
-    if (uninitialized) {
-        valueEditable = valueEnabled = false;
-        str << ",valueencoded=\"0\",value=\"<not in scope>\"";
+    bool valueEditable = !uninitialized;
+    bool valueEnabled = !uninitialized;
+
+    const std::wstring value = displayValue(ctx);
+    // ASCII or base64?
+    if (isSevenBitClean(value.c_str(), value.size())) {
+        str << ",valueencoded=\"0\",value=\"" << gdbmiWStringFormat(value) << '"';
     } else {
-        ULONG obtainedSize = 0;
-        if (const wchar_t *wbuf = getValue(&obtainedSize)) {
-            const ULONG valueSize = obtainedSize - 1;
-            // ASCII or base64?
-            if (isSevenBitClean(wbuf, valueSize)) {
-                std::wstring value = wbuf;
-                fixValue(t, &value);
-                str << ",valueencoded=\"0\",value=\"" << gdbmiWStringFormat(value) << '"';
-            } else {
-                str << ",valueencoded=\"2\",value=\"";
-                base64Encode(str, reinterpret_cast<const unsigned char *>(wbuf), valueSize * sizeof(wchar_t));
-                str << '"';
-            }
-            delete [] wbuf;
-        }
+        str << ",valueencoded=\"2\",value=\"";
+        base64Encode(str, reinterpret_cast<const unsigned char *>(value.c_str()), value.size() * sizeof(wchar_t));
+        str << '"';
     }
+
     // Children: Dump all known or subelements (guess).
     const VectorIndexType childCountGuess = uninitialized ? 0 :
              (m_children.empty() ? m_parameters.SubElements : m_children.size());
@@ -587,39 +583,32 @@ void SymbolGroupNode::dump(std::ostream &str, unsigned child, unsigned depth,
     str << ",valueenabled=\"" << (valueEnabled ? "true" : "false") << '"'
         << ",valueeditable=\"" << (valueEditable ? "true" : "false") << '"'
         << ",numchild=\"" << childCountGuess << '"';
-    if (!uninitialized && !m_children.empty()) {
-        str << ",children=[";
-        if (humanReadable)
-            str << '\n';
-    }
 }
 
-void SymbolGroupNode::dumpChildrenVisited(std::ostream &str, bool humanReadable) const
-{
-    if (!m_children.empty())
-        str << ']';
-    str << '}';
-    if (humanReadable)
-        str << '\n';
-
-}
-bool SymbolGroupNode::accept(SymbolGroupNodeVisitor &visitor, unsigned child, unsigned depth) const
+bool SymbolGroupNode::accept(SymbolGroupNodeVisitor &visitor, unsigned child, unsigned depth)
 {
     // If we happen to be the root node, just skip over
     const bool invisibleRoot = m_index == DEBUG_ANY_ID;
     const unsigned childDepth = invisibleRoot ? 0 : depth + 1;
 
-    if (!invisibleRoot) { // Visit us and move index forward.
-        if (visitor.visit(this, child, depth))
-            return true;
+    const SymbolGroupNodeVisitor::VisitResult vr =
+            invisibleRoot ? SymbolGroupNodeVisitor::VisitContinue :
+                            visitor.visit(this, child, depth);
+    switch (vr) {
+    case SymbolGroupNodeVisitor::VisitStop:
+        return true;
+    case SymbolGroupNodeVisitor::VisitSkipChildren:
+        break;
+    case SymbolGroupNodeVisitor::VisitContinue: {
+        const unsigned childCount = unsigned(m_children.size());
+        for (unsigned c = 0; c < childCount; c++)
+            if (m_children.at(c)->accept(visitor, c, childDepth))
+                return true;
+        if (!invisibleRoot)
+            visitor.childrenVisited(this, depth);
+    }
+        break;
     }
-
-    const unsigned childCount = unsigned(m_children.size());
-    for (unsigned c = 0; c < childCount; c++)
-        if (m_children.at(c)->accept(visitor, c, childDepth))
-            return true;
-    if (!invisibleRoot)
-        visitor.childrenVisited(this, depth);
     return false;
 }
 
@@ -630,13 +619,25 @@ void SymbolGroupNode::debug(std::ostream &str, unsigned verbosity, unsigned dept
     if (const VectorIndexType childCount = m_children.size())
         str << ", Children=" << childCount;
     str << ' ' << m_parameters;
-    if (m_flags)
+    if (m_flags) {
         str << " node-flags=" << m_flags;
+        if (m_flags & Uninitialized)
+            str << " UNINITIALIZED";
+        if (m_flags & DumperNotApplicable)
+            str << " DumperNotApplicable";
+        if (m_flags & DumperOk)
+            str << " DumperOk";
+        if (m_flags & DumperFailed)
+            str << " DumperFailed";
+        if (m_flags & ExpandedByDumper)
+            str << " ExpandedByDumper";
+        str << ' ';
+    }
     if (verbosity) {
         str << ",name=\"" << m_name << "\", Address=0x" << std::hex << address() << std::dec
             << " Type=\"" << type() << '"';
         if (!(m_flags & Uninitialized))
-            str << "\" Value=\"" << gdbmiWStringFormat(rawValue()) << '"';
+            str << "\" Value=\"" << gdbmiWStringFormat(symbolGroupRawValue()) << '"';
     }
     str << '\n';
 }
@@ -646,9 +647,12 @@ bool SymbolGroupNode::expand(std::string *errorMessage)
 {
     if (::debug > 1)
         DebugPrint() << "SymbolGroupNode::expand "  << m_name << ' ' << m_index;
-    if (!m_children.empty())
+    if (isExpanded()) {
+        // Clear the flag indication dumper expansion on a second, explicit request
+        clearFlags(ExpandedByDumper);
         return true;
-    if (m_parameters.SubElements == 0) {
+    }
+    if (!canExpand()) {
         *errorMessage = "No subelements to expand in node: " + fullIName();
         return false;
     }
@@ -684,10 +688,10 @@ static inline std::string msgNotFound(const std::string &nodeName)
     return str.str();
 }
 
-std::string SymbolGroup::dump(bool humanReadable) const
+std::string SymbolGroup::dump(const SymbolGroupValueContext &ctx, bool humanReadable) const
 {
     std::ostringstream str;
-    DumpSymbolGroupNodeVisitor visitor(str, humanReadable);
+    DumpSymbolGroupNodeVisitor visitor(str, ctx, humanReadable);
     if (humanReadable)
         str << '\n';
     str << '[';
@@ -697,33 +701,43 @@ std::string SymbolGroup::dump(bool humanReadable) const
 }
 
 // Dump a node, potentially expand
-std::string SymbolGroup::dump(const std::string &name, bool humanReadable, std::string *errorMessage)
+std::string SymbolGroup::dump(const std::string &iname, const SymbolGroupValueContext &ctx, bool humanReadable, std::string *errorMessage)
 {
-    SymbolGroupNode *const node = find(name);
+    SymbolGroupNode *const node = find(iname);
     if (node == 0) {
-        *errorMessage = msgNotFound(name);
+        *errorMessage = msgNotFound(iname);
         return std::string();
     }
-    if (node->subElements() && node->children().empty()) {
-        if (!expand(name, errorMessage))
+    if (node->isExpanded()) { // Mark expand request by watch model
+        node->clearFlags(SymbolGroupNode::ExpandedByDumper);
+    } else {
+        if (node->canExpand() && !node->expand(errorMessage))
             return false;
     }
     std::ostringstream str;
     if (humanReadable)
         str << '\n';
-    DumpSymbolGroupNodeVisitor visitor(str, humanReadable);
+    DumpSymbolGroupNodeVisitor visitor(str, ctx, humanReadable);
     str << '[';
     node->accept(visitor, 0, 0);
     str << ']';
     return str.str();
 }
 
-std::string SymbolGroup::debug(unsigned verbosity) const
+std::string SymbolGroup::debug(const std::string &iname, unsigned verbosity) const
 {
     std::ostringstream str;
     str << '\n';
     DebugSymbolGroupNodeVisitor visitor(str, verbosity);
-    accept(visitor);
+    if (iname.empty()) {
+        accept(visitor);
+    } else {
+        if (SymbolGroupNode *const node = find(iname)) {
+            node->accept(visitor, 0, 0);
+        } else {
+            str << msgNotFound(iname);
+        }
+    }
     return str.str();
 }
 
@@ -807,7 +821,7 @@ void SymbolGroup::markUninitialized(const std::vector<std::string> &uniniNodes)
         const SymbolGroupNodePtrVector::const_iterator childrenEnd = m_root->children().end();
         for (SymbolGroupNodePtrVector::const_iterator it = m_root->children().begin(); it != childrenEnd; ++it) {
             if (std::find(unIniNodesBegin, unIniNodesEnd, (*it)->fullIName()) != unIniNodesEnd)
-                (*it)->setFlags((*it)->flags() | SymbolGroupNode::Uninitialized);
+                (*it)->addFlags(SymbolGroupNode::Uninitialized);
         }
     }
 }
@@ -907,27 +921,53 @@ DebugSymbolGroupNodeVisitor::DebugSymbolGroupNodeVisitor(std::ostream &os, unsig
 {
 }
 
-bool DebugSymbolGroupNodeVisitor::visit(const SymbolGroupNode *node,
-                                        unsigned /* child */, unsigned depth)
+SymbolGroupNodeVisitor::VisitResult
+    DebugSymbolGroupNodeVisitor::visit(SymbolGroupNode *node,
+                                       unsigned /* child */, unsigned depth)
 {
     node->debug(m_os, m_verbosity, depth);
-    return false;
+    return VisitContinue;
 }
 
 // --------------------- DumpSymbolGroupNodeVisitor
 DumpSymbolGroupNodeVisitor::DumpSymbolGroupNodeVisitor(std::ostream &os,
+                                                       const SymbolGroupValueContext &context,
                                                        bool humanReadable) :
-    m_os(os), m_humanReadable(humanReadable)
+    m_os(os), m_humanReadable(humanReadable),m_context(context), m_visitChildren(false)
 {
 }
 
-bool DumpSymbolGroupNodeVisitor::visit(const SymbolGroupNode *node, unsigned child, unsigned depth)
+SymbolGroupNodeVisitor::VisitResult
+    DumpSymbolGroupNodeVisitor::visit(SymbolGroupNode *node, unsigned child, unsigned depth)
 {
-    node->dump(m_os, child, depth, m_humanReadable);
-    return false;
+    // Recurse to children if expanded by explicit watchmodel request
+    // and initialized.
+    const unsigned flags = node->flags();
+    m_visitChildren = node->isExpanded()
+            && (flags & (SymbolGroupNode::Uninitialized|SymbolGroupNode::ExpandedByDumper)) == 0;
+
+    // Do not recurse into children unless the node was expanded by the watch model
+    if (child)
+        m_os << ','; // Separator in parents list
+    if (m_humanReadable) {
+        m_os << '\n';
+        indentStream(m_os, depth * 2);
+    }
+    m_os << '{';
+    node->dump(m_os, m_context);
+    if (m_visitChildren) { // open children array
+        m_os << ",children=[";
+    } else {               // No children, close array.
+        m_os << '}';
+    }
+    if (m_humanReadable)
+        m_os << '\n';
+    return m_visitChildren ? VisitContinue : VisitSkipChildren;
 }
 
-void DumpSymbolGroupNodeVisitor::childrenVisited(const SymbolGroupNode *node, unsigned)
+void DumpSymbolGroupNodeVisitor::childrenVisited(const SymbolGroupNode *, unsigned)
 {
-    node->dumpChildrenVisited(m_os, m_humanReadable);
+    m_os << "]}"; // Close children array and self
+    if (m_humanReadable)
+        m_os << '\n';
 }
index 59d8c47..950066b 100644 (file)
@@ -40,14 +40,27 @@ std::ostream &operator<<(std::ostream &, const DEBUG_SYMBOL_PARAMETERS&p);
 
 class SymbolGroupNodeVisitor;
 class SymbolGroup;
+struct SymbolGroupValueContext;
+
+// Thin wrapper around a symbol group entry. Provides accessors for fixed-up
+// symbol group value and a dumping facility triggered by dump()/displayValue()
+// calling dumpSimpleType() based on SymbolGroupValue expressions. These values
+// values should be displayed, still allowing for expansion of the structure
+// in the debugger. Evaluating the dumpers might expand symbol nodes, which are
+// then marked as 'ExpandedByDumper'. This stops the dump recursion to prevent
+// outputting data that were not explicitly expanded by the watch handler.
 
-// Thin wrapper around a symbol group entry.
 class SymbolGroupNode {
     SymbolGroupNode(const SymbolGroupNode&);
     SymbolGroupNode& operator=(const SymbolGroupNode&);
 public:
     enum Flags {
-        Uninitialized = 0x1
+        Uninitialized = 0x1,
+        DumperNotApplicable = 0x2, // No dumper available for type
+        DumperOk = 0x4,     // Internal dumper ran, value set
+        DumperFailed = 0x8, // Internal dumper failed
+        DumperMask = DumperNotApplicable|DumperOk|DumperFailed,
+        ExpandedByDumper = 0x10
     };
     typedef std::vector<DEBUG_SYMBOL_PARAMETERS> SymbolParameterVector;
     typedef std::vector<SymbolGroupNode *> SymbolGroupNodePtrVector;
@@ -75,35 +88,39 @@ public:
 
     const SymbolGroupNodePtrVector &children() const { return m_children; }
     SymbolGroupNode *childAt(unsigned) const;
+
+    unsigned indexByIName(const char *) const; // (unsigned(-1) on failure
     SymbolGroupNode *childByIName(const char *) const;
 
     const SymbolGroupNode *parent() const { return m_parent; }
 
     // I/O: Gdbmi dump for Visitors
-    void dump(std::ostream &str, unsigned child, unsigned depth,
-              bool humanReadable) const;
-    void dumpChildrenVisited(std::ostream &str, bool humanReadable) const;
+    void dump(std::ostream &str, const SymbolGroupValueContext &ctx);
     // I/O: debug for Visitors
     void debug(std::ostream &os, unsigned verbosity, unsigned depth) const;
 
-    std::wstring rawValue() const;
-    std::wstring fixedValue() const;
+    std::wstring symbolGroupRawValue() const;
+    std::wstring symbolGroupFixedValue() const;
+    std::wstring displayValue(const SymbolGroupValueContext &ctx);
+
     std::string type() const;
     ULONG64 address() const;
 
-    bool accept(SymbolGroupNodeVisitor &visitor, unsigned child, unsigned depth) const;
+    bool accept(SymbolGroupNodeVisitor &visitor, unsigned child, unsigned depth);
 
     bool expand(std::string *errorMessage);
+    bool isExpanded() const { return !m_children.empty(); }
+    bool canExpand() const { return m_parameters.SubElements > 0; }
 
     ULONG subElements() const { return m_parameters.SubElements; }
     ULONG index() const { return m_index; }
 
     unsigned flags() const     { return m_flags; }
     void setFlags(unsigned f)  { m_flags = f; }
+    void addFlags(unsigned f)  { m_flags |= f; }
+    void clearFlags(unsigned f)  { m_flags &= ~f; }
 
 private:
-    // Return allocated wide string array of value
-    wchar_t *getValue(ULONG *obtainedSize = 0) const;
     bool isArrayElement() const;
     // Notify about expansion of a node, shift indexes
     bool notifyExpanded(ULONG index, ULONG insertedCount);
@@ -116,6 +133,7 @@ private:
     const std::string m_name;
     const std::string m_iname;
     unsigned m_flags;
+    std::wstring m_dumperValue;
 };
 
 /* Visitor that takes care of iterating over the nodes
@@ -133,8 +151,15 @@ protected:
 public:
     virtual ~SymbolGroupNodeVisitor() {}
 
+protected:
+    enum VisitResult {
+        VisitContinue,
+        VisitSkipChildren,
+        VisitStop
+    };
+
 private:
-    virtual bool visit(const SymbolGroupNode *node, unsigned child, unsigned depth) = 0;
+    virtual VisitResult visit(SymbolGroupNode *node, unsigned child, unsigned depth) = 0;
     // Helper for formatting output.
     virtual void childrenVisited(const SymbolGroupNode * /* node */, unsigned /* depth */) {}
 };
@@ -167,10 +192,11 @@ public:
     ~SymbolGroup();
 
     // Dump all
-    std::string dump(bool humanReadable = false) const;
+    std::string dump(const SymbolGroupValueContext &ctx, bool humanReadable = false) const;
     // Expand node and dump
-    std::string dump(const std::string &name, bool humanReadable, std::string *errorMessage);
-    std::string debug(unsigned verbosity = 0) const;
+    std::string dump(const std::string &iname, const SymbolGroupValueContext &ctx,
+                     bool humanReadable, std::string *errorMessage);
+    std::string debug(const std::string &iname = std::string(), unsigned verbosity = 0) const;
 
     unsigned frame() const { return m_frame; }
     ULONG threadId() const { return m_threadId; }
@@ -221,7 +247,7 @@ public:
     explicit DebugSymbolGroupNodeVisitor(std::ostream &os, unsigned verbosity = 0);
 
 private:
-    virtual bool visit(const SymbolGroupNode *node, unsigned child, unsigned depth);
+    virtual VisitResult visit(SymbolGroupNode *node, unsigned child, unsigned depth);
 
     std::ostream &m_os;
     const unsigned m_verbosity;
@@ -230,14 +256,18 @@ private:
 // Gdbmi dump output visitor.
 class DumpSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor {
 public:
-    explicit DumpSymbolGroupNodeVisitor(std::ostream &os, bool humanReadable);
+    explicit DumpSymbolGroupNodeVisitor(std::ostream &os,
+                                        const SymbolGroupValueContext &context,
+                                        bool humanReadable);
 
 private:
-    virtual bool visit(const SymbolGroupNode *node, unsigned child, unsigned depth);
+    virtual VisitResult visit(SymbolGroupNode *node, unsigned child, unsigned depth);
     virtual void childrenVisited(const SymbolGroupNode *  node, unsigned depth);
 
     std::ostream &m_os;
     const bool m_humanReadable;
+    const SymbolGroupValueContext &m_context;
+    bool m_visitChildren;
 };
 
 #endif // SYMBOLGROUP_H
index 9e279e5..f89db16 100644 (file)
 #include "symbolgroup.h"
 #include "stringutils.h"
 
-SymbolGroupValueContext::SymbolGroupValueContext(CIDebugDataSpaces *ds)
-    : dataspaces(ds)
-{
-}
-
-SymbolGroupValueContext::SymbolGroupValueContext() : dataspaces(0)
-{
-}
-
 SymbolGroupValue::SymbolGroupValue(SymbolGroupNode *node,
                                    const SymbolGroupValueContext &ctx) :
     m_node(node), m_context(ctx)
@@ -56,9 +47,34 @@ bool SymbolGroupValue::isValid() const
     return m_node != 0 && m_context.dataspaces != 0;
 }
 
+SymbolGroupValue SymbolGroupValue::operator[](unsigned index) const
+{
+    if (ensureExpanded())
+        if (index < m_node->children().size())
+            return SymbolGroupValue(m_node->children().at(index), m_context);
+    return SymbolGroupValue();
+}
+
+bool SymbolGroupValue::ensureExpanded() const
+{
+    if (!isValid() || !m_node->canExpand())
+        return false;
+
+    if (m_node->isExpanded())
+        return true;
+
+    // Set a flag indicating the node was expanded by SymbolGroupValue
+    // and not by an explicit request from the watch model.
+    if (m_node->expand(&m_errorMessage)) {
+        m_node->addFlags(SymbolGroupNode::ExpandedByDumper);
+        return true;
+    }
+    return false;
+}
+
 SymbolGroupValue SymbolGroupValue::operator[](const char *name) const
 {
-    if (isValid() && m_node->expand(&m_errorMessage))
+    if (ensureExpanded())
         if (SymbolGroupNode *child = m_node->childByIName(name))
             return SymbolGroupValue(child, m_context);
     return SymbolGroupValue();
@@ -71,7 +87,19 @@ std::string SymbolGroupValue::type() const
 
 std::wstring SymbolGroupValue::value() const
 {
-    return isValid() ? m_node->fixedValue() : std::wstring();
+    return isValid() ? m_node->symbolGroupFixedValue() : std::wstring();
+}
+
+double SymbolGroupValue::floatValue(double defaultValue) const
+{
+    double f = defaultValue;
+    if (isValid()) {
+        std::wistringstream str(value());
+        str >> f;
+        if (str.fail())
+            f = defaultValue;
+    }
+    return f;
 }
 
 int SymbolGroupValue::intValue(int defaultValue) const
@@ -136,26 +164,138 @@ std::string SymbolGroupValue::error() const
     return m_errorMessage;
 }
 
+static const char stdStringTypeC[] = "class std::basic_string<char,std::char_traits<char>,std::allocator<char> >";
+static const char stdWStringTypeC[] = "class std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >";
+
 // Dump a QString.
-static bool dumpQString(const SymbolGroupValue &v, std::wstring *s)
-{
-    if (endsWith(v.type(), "QString")) {
-        if (SymbolGroupValue d = v["d"]) {
-            if (SymbolGroupValue sizeValue = d["size"]) {
-                const int size = sizeValue.intValue();
-                if (size >= 0) {
-                    *s = d["data"].wcharPointerData(size);
-                    return true;
-                }
+static unsigned dumpQString(const std::string &type, const SymbolGroupValue &v, std::wstring *s)
+{
+    if (!endsWith(type, "QString")) // namespaced Qt?
+        return SymbolGroupNode::DumperNotApplicable;
+
+    if (SymbolGroupValue d = v["d"]) {
+        if (SymbolGroupValue sizeValue = d["size"]) {
+            const int size = sizeValue.intValue();
+            if (size >= 0) {
+                *s = d["data"].wcharPointerData(size);
+                return SymbolGroupNode::DumperOk;
             }
         }
     }
-    return false;
+    return SymbolGroupNode::DumperFailed;
+}
+
+// Dump a QByteArray
+static unsigned dumpQByteArray(const std::string &type, const SymbolGroupValue &v, std::wstring *s)
+{
+    if (!endsWith(type, "QByteArray")) // namespaced Qt?
+        return SymbolGroupNode::DumperNotApplicable;
+    // TODO: More sophisticated dumping of binary data?
+    if (SymbolGroupValue data = v["d"]["data"]) {
+        *s = data.value();
+        return SymbolGroupNode::DumperOk;
+    }
+    return SymbolGroupNode::DumperFailed;
+}
+
+// Dump a rectangle in X11 syntax
+template <class T>
+inline void dumpRect(std::wostringstream &str, T x, T y, T width, T height)
+{
+    str << width << 'x' << height;
+    if (x >= 0)
+        str << '+';
+    str << x;
+    if (y >= 0)
+        str << '+';
+    str << y;
+}
+
+template <class T>
+inline void dumpRectPoints(std::wostringstream &str, T x1, T y1, T x2, T y2)
+{
+    dumpRect(str, x1, y1, (x2 - x1), (y2 - y1));
+}
+
+// Dump Qt's simple geometrical types
+static unsigned dumpQtGeometryTypes(const std::string &type, const SymbolGroupValue &v, std::wstring *s)
+{
+    if (endsWith(type, "QSize") || endsWith(type, "QSizeF")) { // namespaced Qt?
+        std::wostringstream str;
+        str << '(' << v["wd"].value() << ", " << v["ht"].value() << ')';
+        *s = str.str();
+        return SymbolGroupNode::DumperOk;
+    }
+    if (endsWith(type, "QPoint") || endsWith(type, "QPointF")) { // namespaced Qt?
+        std::wostringstream str;
+        str << '(' << v["xp"].value() << ", " << v["yp"].value() << ')';
+        *s = str.str();
+        return SymbolGroupNode::DumperOk;
+    }
+    if (endsWith(type, "QLine") || endsWith(type, "QLineF")) { // namespaced Qt?
+        const SymbolGroupValue p1 = v["pt1"];
+        const SymbolGroupValue p2 = v["pt2"];
+        if (p1 && p2) {
+            std::wostringstream str;
+            str << '(' << p1["xp"].value() << ", " << p1["yp"].value() << ") ("
+                 << p2["xp"].value() << ", " << p2["yp"].value() << ')';
+            *s = str.str();
+            return SymbolGroupNode::DumperOk;
+        }
+        return SymbolGroupNode::DumperFailed;
+    }
+    if (endsWith(type, "QRect")) {
+        std::wostringstream str;
+        dumpRectPoints(str, v["x1"].intValue(), v["y1"].intValue(), v["x2"].intValue(), v["y2"].intValue());
+        *s = str.str();
+        return SymbolGroupNode::DumperOk;
+    }
+    if (endsWith(type, "QRectF")) {
+        std::wostringstream str;
+        dumpRect(str, v["xp"].floatValue(), v["yp"].floatValue(), v["w"].floatValue(), v["h"].floatValue());
+        *s = str.str();
+        return SymbolGroupNode::DumperOk;
+    }
+    return SymbolGroupNode::DumperNotApplicable;
+}
+
+// Dump a std::string.
+static unsigned dumpStdString(const std::string &type, const SymbolGroupValue &v, std::wstring *s)
+{
+    if (type != stdStringTypeC && type != stdWStringTypeC)
+        return SymbolGroupNode::DumperNotApplicable;
+    // MSVC 2010: Access Bx/_Buf in base class
+    SymbolGroupValue buf = v[unsigned(0)]["_Bx"]["_Buf"];
+    if (!buf) // MSVC2008: Bx/Buf are members
+        buf = v["_Bx"]["_Buf"];
+    if (buf) {
+        *s = buf.value();
+        return SymbolGroupNode::DumperOk;
+    }
+    return SymbolGroupNode::DumperFailed;
 }
 
 // Dump builtin simple types using SymbolGroupValue expressions.
-bool dumpSimpleType(SymbolGroupNode  *n, const SymbolGroupValueContext &ctx, std::wstring *s)
+unsigned dumpSimpleType(SymbolGroupNode  *n, const SymbolGroupValueContext &ctx, std::wstring *s)
 {
+    // Check for class types and strip pointer types (references appear as pointers as well)
+    std::string type = n->type();
+    if (type.compare(0, 6, "class ") != 0)
+        return SymbolGroupNode::DumperNotApplicable;
+    if (endsWith(type, " *"))
+        type.erase(type.size() - 2, 2);
     const SymbolGroupValue v(n, ctx);
-    return dumpQString(v, s);
+    unsigned rc = dumpQString(type, v, s);
+    if (rc != SymbolGroupNode::DumperNotApplicable)
+        return rc;
+    rc = dumpQByteArray(type, v, s);
+    if (rc != SymbolGroupNode::DumperNotApplicable)
+        return rc;
+    rc = dumpStdString(type, v, s);
+    if (rc != SymbolGroupNode::DumperNotApplicable)
+        return rc;
+    rc = dumpQtGeometryTypes(type, v, s);
+    if (rc != SymbolGroupNode::DumperNotApplicable)
+        return rc;
+    return rc;
 }
index bb18ab2..b581d7d 100644 (file)
@@ -39,8 +39,8 @@ class SymbolGroupNode;
 // Structure to pass all IDebug interfaces used for SymbolGroupValue
 struct SymbolGroupValueContext
 {
-    SymbolGroupValueContext(CIDebugDataSpaces *ds);
-    SymbolGroupValueContext();
+    SymbolGroupValueContext(CIDebugDataSpaces *ds) : dataspaces(ds) {}
+    SymbolGroupValueContext::SymbolGroupValueContext() : dataspaces(0) {}
 
     CIDebugDataSpaces *dataspaces;
 };
@@ -58,11 +58,14 @@ public:
     operator bool() const { return isValid(); }
     bool isValid() const;
 
+    // Access children by name or index (0-based)
     SymbolGroupValue operator[](const char *name) const;
+    SymbolGroupValue operator[](unsigned) const;
 
     std::string type() const;
     std::wstring value() const;
     int intValue(int defaultValue = -1) const;
+    double floatValue(double defaultValue = -999) const;
     ULONG64 pointerValue(ULONG64 defaultValue = 0) const;
     // Return allocated array of data pointed to
     unsigned char *pointerData(unsigned length) const;
@@ -72,12 +75,15 @@ public:
     std::string error() const;
 
 private:
+    bool ensureExpanded() const;
+
     SymbolGroupNode *m_node;
     SymbolGroupValueContext m_context;
     mutable std::string m_errorMessage;
 };
 
-// Dump builtin simple types using SymbolGroupValue expressions.
-bool dumpSimpleType(SymbolGroupNode  *n, const SymbolGroupValueContext &ctx, std::wstring *s);
+// Dump builtin simple types using SymbolGroupValue expressions,
+// returning SymbolGroupNode dumper flags.
+unsigned dumpSimpleType(SymbolGroupNode  *n, const SymbolGroupValueContext &ctx, std::wstring *s);
 
 #endif // SYMBOLGROUPVALUE_H
index 1cb112f..727c812 100644 (file)
 #define _USE_MATH_DEFINES
 #include <math.h>
 #include <QDebug>
+#include <QRect>
+#include <QRectF>
+#include <QLine>
+#include <QLineF>
+#include <QPoint>
+#include <QPointF>
+#include <QSize>
+#include <QSizeF>
+
 #include <QThread>
 #include <string>
-#include <list>
 #include <set>
 #include <QLibrary>
 #include <QLibraryInfo>
@@ -78,7 +86,7 @@ void MainWindow::simpleBP(int inc, const QString &inx)
 {    
     int array[2] = {1,2};
     m_w++;
-    QString x = "h\"allo";
+    QString x = QLatin1String("h\344all\366");
     QString *xp = &x;
     qDebug() << inc << inx << *xp;
     Q_UNUSED(array)
@@ -379,3 +387,20 @@ void Foo::MainWindow::on_actionStdTypes_triggered()
     std::vector<std::string> stringVector(1, "bla");
     std::vector<std::wstring> wStringVector(1, L"bla");
 }
+
+void Foo::MainWindow::on_actionVariousQtTypes_triggered()
+{
+    const QByteArray ba = "hallo\t";
+    QSize size = QSize(42, 43);
+    QSizeF sizeF(size);
+    QPoint p1 = QPoint(42, 43);
+    QPoint p2 = QPoint(100, 100);
+    QLine line(p1, p2);
+    QPointF p1f(p1);
+    QPointF p2f(p2);
+    QLineF linef(p1f, p2f);
+    QRect rect(p1, p2);
+    QRectF rectf(rect);
+    qDebug() << sizeF << linef << rectf;
+
+}
index 617c404..077cf15 100644 (file)
@@ -71,6 +71,8 @@ private slots:
 
     void on_actionStdTypes_triggered();
 
+    void on_actionVariousQtTypes_triggered();
+
 private:
     void terminateThread();
     int m_w;
index 892b1b0..639fd6c 100644 (file)
@@ -20,7 +20,7 @@
      <x>0</x>
      <y>0</y>
      <width>600</width>
-     <height>27</height>
+     <height>26</height>
     </rect>
    </property>
    <widget class="QMenu" name="menuFile">
@@ -47,6 +47,7 @@
    <addaction name="actionScopes"/>
    <addaction name="actionLongString"/>
    <addaction name="actionStdTypes"/>
+   <addaction name="actionVariousQtTypes"/>
   </widget>
   <widget class="QStatusBar" name="statusBar"/>
   <widget class="QToolBar" name="toolBar">
     <string>stdTypes</string>
    </property>
   </action>
+  <action name="actionVariousQtTypes">
+   <property name="text">
+    <string>variousQtTypes</string>
+   </property>
+  </action>
  </widget>
  <layoutdefault spacing="6" margin="11"/>
  <resources/>