diff --git a/src/libs/qtcreatorcdbext/containers.cpp b/src/libs/qtcreatorcdbext/containers.cpp
index 13d7b7b452619c6bd4653c5cfb10ff2afbac3124..e8e25fe50bd992ab51a725facfee745207a491fd 100644
--- a/src/libs/qtcreatorcdbext/containers.cpp
+++ b/src/libs/qtcreatorcdbext/containers.cpp
@@ -35,6 +35,8 @@
#include <functional>
typedef AbstractSymbolGroupNode::AbstractSymbolGroupNodePtrVector AbstractSymbolGroupNodePtrVector;
+typedef std::vector<SymbolGroupValue> SymbolGroupValueVector;
+typedef std::vector<int>::size_type VectorIndexType;
// Read a pointer array from debuggee memory (ULONG64/32 according pointer size)
static void *readPointerArray(ULONG64 address, unsigned count, const SymbolGroupValueContext &ctx)
@@ -394,12 +396,152 @@ static inline AbstractSymbolGroupNodePtrVector
innerType, count);
}
+// Return the list of buckets of a 'QHash<>' as 'QHashData::Node *' values from
+// the list of addresses passed in
+template<class AddressType>
+SymbolGroupValueVector hashBuckets(SymbolGroup *sg, const std::string hashNodeType,
+ const AddressType *pointerArray,
+ int numBuckets,
+ AddressType ePtr,
+ const SymbolGroupValueContext &ctx)
+{
+ SymbolGroupValueVector rc;
+ rc.reserve(numBuckets);
+ const AddressType *end = pointerArray + numBuckets;
+ std::string errorMessage;
+ // Skip 'e' special values as they are used as placeholder for reserve(d)
+ // empty array elements.
+ for (const AddressType *p = pointerArray; p < end; p++) {
+ if (*p != ePtr) {
+ const std::string name = pointedToSymbolName(*p, hashNodeType);
+ if (SymbolGroupNode *child = sg->addSymbol(name, std::string(), &errorMessage)) {
+ rc.push_back(SymbolGroupValue(child, ctx));
+ } else {
+ return std::vector<SymbolGroupValue>();
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+// Return real node type of a QHash: "class QHash<K,V>" -> [struct] "QHashNode<K,V>";
+static inline std::string qHashNodeType(std::string qHashType)
+{
+ qHashType.erase(0, 6); // Strip "class ";
+ const std::string::size_type pos = qHashType.find('<');
+ if (pos != std::string::npos)
+ qHashType.insert(pos, "Node");
+ return qHashType;
+}
+
+// Return up to count nodes of type "QHashNode<K,V>" of a "class QHash<K,V>".
+SymbolGroupValueVector qHashNodes(const SymbolGroupValue &v,
+ VectorIndexType count)
+{
+ if (!count)
+ return SymbolGroupValueVector();
+ const SymbolGroupValue hashData = v["d"];
+ // 'e' is used as a special value to indicate empty hash buckets in the array.
+ const ULONG64 ePtr = v["e"].pointerValue();
+ if (!hashData || !ePtr)
+ return SymbolGroupValueVector();
+ // Retrieve the array of buckets of 'd'
+ const int numBuckets = hashData["numBuckets"].intValue();
+ const ULONG64 bucketArray = hashData["buckets"].pointerValue();
+ if (numBuckets <= 0 || !bucketArray)
+ return SymbolGroupValueVector();
+ void *bucketPointers = readPointerArray(bucketArray, numBuckets, v.context());
+ if (!bucketPointers)
+ return SymbolGroupValueVector();
+ // Get list of buckets (starting elements of 'QHashData::Node')
+ const std::string dummyNodeType = "QHashData::Node";
+ const SymbolGroupValueVector buckets = SymbolGroupValue::pointerSize() == 8 ?
+ hashBuckets(v.node()->symbolGroup(), dummyNodeType,
+ reinterpret_cast<const ULONG64 *>(bucketPointers), numBuckets,
+ ePtr, v.context()) :
+ hashBuckets(v.node()->symbolGroup(), dummyNodeType,
+ reinterpret_cast<const ULONG32 *>(bucketPointers), numBuckets,
+ ULONG32(ePtr), v.context());
+ delete [] bucketPointers ;
+ // Generate the list 'QHashData::Node *' by iterating over the linked list of
+ // nodes starting at each bucket. Using the 'QHashData::Node *' instead of
+ // the 'QHashNode<K,T>' is much faster. Each list has a trailing, unused
+ // dummy element.
+ SymbolGroupValueVector dummyNodeList;
+ dummyNodeList.reserve(count);
+ bool notEnough = true;
+ const SymbolGroupValueVector::const_iterator ncend = buckets.end();
+ for (SymbolGroupValueVector::const_iterator it = buckets.begin(); notEnough && it != ncend; ++it) {
+ for (SymbolGroupValue l = *it; notEnough && l ; ) {
+ const SymbolGroupValue next = l["next"];
+ if (next && next.pointerValue()) { // Stop at trailing dummy element
+ dummyNodeList.push_back(l);
+ if (dummyNodeList.size() >= count) // Stop at maximum count
+ notEnough = false;
+ } else {
+ break;
+ }
+ }
+ }
+ // Finally convert them into real nodes 'QHashNode<K,V> (potentially expensive)
+ const std::string nodeType = qHashNodeType(v.type());
+ SymbolGroupValueVector nodeList;
+ nodeList.reserve(count);
+ const SymbolGroupValueVector::const_iterator dcend = dummyNodeList.end();
+ for (SymbolGroupValueVector::const_iterator it = dummyNodeList.begin(); it != dcend; ++it) {
+ if (const SymbolGroupValue n = (*it).typeCast(nodeType.c_str())) {
+ nodeList.push_back(n);
+ } else {
+ return SymbolGroupValueVector();
+ }
+ }
+ return nodeList;
+}
+
+// QSet<>: Contains a 'QHash<key, QHashDummyValue>' as member 'q_hash'.
+// Just dump the keys as an array.
static inline AbstractSymbolGroupNodePtrVector
- qHashChildList(const SymbolGroupValue &, int count)
+ qSetChildList(const SymbolGroupValue &v, VectorIndexType count)
+{
+ const SymbolGroupValue qHash = v["q_hash"];
+ AbstractSymbolGroupNodePtrVector rc;
+ if (!count || !qHash)
+ return rc;
+ const SymbolGroupValueVector nodes = qHashNodes(qHash, count);
+ if (nodes.size() != VectorIndexType(count))
+ return rc;
+ rc.reserve(count);
+ for (int i = 0; i < count; i++) {
+ if (const SymbolGroupValue key = nodes.at(i)["key"]) {
+ rc.push_back(ReferenceSymbolGroupNode::createArrayNode(i, key.node()));
+ } else {
+ return AbstractSymbolGroupNodePtrVector();
+ }
+ }
+ return rc;
+}
+
+// QHash<>: Add with fake map nodes.
+static inline AbstractSymbolGroupNodePtrVector
+ qHashChildList(const SymbolGroupValue &v, VectorIndexType count)
{
AbstractSymbolGroupNodePtrVector rc;
if (!count)
return rc;
+ const SymbolGroupValueVector nodes = qHashNodes(v, count);
+ if (nodes.size() != count)
+ return rc;
+ rc.reserve(count);
+ for (int i = 0; i < count; i++) {
+ const SymbolGroupValue &mapNode = nodes.at(i);
+ const SymbolGroupValue key = mapNode["key"];
+ const SymbolGroupValue value = mapNode["value"];
+ if (!key || !value)
+ return AbstractSymbolGroupNodePtrVector();
+ rc.push_back(MapNodeSymbolGroupNode::create(i, mapNode.address(),
+ mapNode.type(), key.node(), value.node()));
+ }
return rc;
}
@@ -429,14 +571,12 @@ AbstractSymbolGroupNodePtrVector containerChildren(SymbolGroupNode *node, int ty
break;
case KT_QHash:
return qHashChildList(SymbolGroupValue(node, ctx), size);
- case KT_QMultiMap:
+ case KT_QMultiHash:
if (const SymbolGroupValue hash = SymbolGroupValue(node, ctx)[unsigned(0)])
return qHashChildList(hash, size);
break;
case KT_QSet:
- if (const SymbolGroupValue qHash = SymbolGroupValue(node, ctx)["q_hash"])
- return qHashChildList(qHash, size);
- break;
+ return qSetChildList(SymbolGroupValue(node, ctx), size);
case KT_QStringList:
if (const SymbolGroupValue qList = SymbolGroupValue(node, ctx)[unsigned(0)])
return qListChildList(qList, size);
diff --git a/src/libs/qtcreatorcdbext/symbolgroup.h b/src/libs/qtcreatorcdbext/symbolgroup.h
index ce042ae7f3bf3abaab21b16fed66c3d1c9f14c26..67be5b52e005450740e7422d4e0be7863e137def 100644
--- a/src/libs/qtcreatorcdbext/symbolgroup.h
+++ b/src/libs/qtcreatorcdbext/symbolgroup.h
@@ -33,10 +33,13 @@
#include "common.h"
#include "symbolgroupnode.h"
-// Thin wrapper around a symbol group storing a tree of expanded symbols rooted on
-// a fake "locals" root element.
-// Provides a find() method based on inames ("locals.this.i1.data") that retrieves
-// that index based on the current expansion state.
+/* A symbol group storing a tree of expanded symbols rooted on
+ * a fake "locals" root element.
+ * Provides a find() method based on inames ("locals.this.i1.data") and
+ * dump() methods used for GDBMI-format dumping and debug helpers.
+ * Qt Creator's WatchModel is fed from this class. It basically represents the
+ * symbol group tree with some additional node types (Reference and Map Node
+ * types. */
class SymbolGroup {
public:
diff --git a/src/libs/qtcreatorcdbext/symbolgroupnode.cpp b/src/libs/qtcreatorcdbext/symbolgroupnode.cpp
index d89fbabd6d94f966b97e888706c2af09eda146b0..64755c50e3ff8950af6f81af820b0afd1038c818 100644
--- a/src/libs/qtcreatorcdbext/symbolgroupnode.cpp
+++ b/src/libs/qtcreatorcdbext/symbolgroupnode.cpp
@@ -1023,6 +1023,20 @@ SymbolGroupNode *SymbolGroupNode::addSymbolByName(const std::string &name,
}
// --------- ReferenceSymbolGroupNode
+
+// Utility returning a pair ('[42]','42') as name/iname pair
+// for a node representing an array index
+typedef std::pair<std::string, std::string> StringStringPair;
+
+static inline StringStringPair arrayIndexNameIname(int index)
+{
+ StringStringPair rc(std::string(), toString(index));
+ rc.first = std::string(1, '[');
+ rc.first += rc.second;
+ rc.first.push_back(']');
+ return rc;
+}
+
ReferenceSymbolGroupNode::ReferenceSymbolGroupNode(const std::string &name,
const std::string &iname,
SymbolGroupNode *referencedNode) :
@@ -1034,11 +1048,8 @@ ReferenceSymbolGroupNode::ReferenceSymbolGroupNode(const std::string &name,
ReferenceSymbolGroupNode *ReferenceSymbolGroupNode::createArrayNode(int index,
SymbolGroupNode *referencedNode)
{
- const std::string iname = toString(index);
- std::string name = std::string(1, '[');
- name += iname;
- name.push_back(']');
- return new ReferenceSymbolGroupNode(name, iname, referencedNode);
+ const StringStringPair nameIname = arrayIndexNameIname(index);
+ return new ReferenceSymbolGroupNode(nameIname.first, nameIname.second, referencedNode);
}
void ReferenceSymbolGroupNode::dump(std::ostream &str, const std::string &visitingFullIname,
@@ -1056,6 +1067,49 @@ void ReferenceSymbolGroupNode::debug(std::ostream &str, const std::string &visit
m_referencedNode->debug(str, visitingFullIname, verbosity, depth);
}
+// ---------------- MapNodeSymbolGroupNode
+MapNodeSymbolGroupNode::MapNodeSymbolGroupNode(const std::string &name,
+ const std::string &iname,
+ ULONG64 address,
+ const std::string &type,
+ AbstractSymbolGroupNode *key,
+ AbstractSymbolGroupNode *value) :
+ BaseSymbolGroupNode(name, iname), m_address(address), m_type(type)
+{
+ addChild(key);
+ addChild(value);
+}
+
+MapNodeSymbolGroupNode
+ *MapNodeSymbolGroupNode::create(int index, ULONG64 address,
+ const std::string &type,
+ SymbolGroupNode *key, SymbolGroupNode *value)
+{
+ const StringStringPair nameIname = arrayIndexNameIname(index);
+ const std::string keyName = "key";
+ ReferenceSymbolGroupNode *keyRN = new ReferenceSymbolGroupNode(keyName, keyName, key);
+ const std::string valueName = "value";
+ ReferenceSymbolGroupNode *valueRN = new ReferenceSymbolGroupNode(valueName, valueName, value);
+ return new MapNodeSymbolGroupNode(nameIname.first, nameIname.second, address, type, keyRN, valueRN);
+}
+
+void MapNodeSymbolGroupNode::dump(std::ostream &str, const std::string &visitingFullIname,
+ const DumpParameters &, const SymbolGroupValueContext &)
+{
+ SymbolGroupNode::dumpBasicData(str, name(), visitingFullIname);
+ if (m_address)
+ str << ",address=\"0x" << std::hex << m_address << '"';
+ str << ",type=\"" << m_type << "\",valueencoded=\"0\",value=\"\",valueenabled=\"false\""
+ ",valueeditable=\"false\",numchild=\"2\"";
+}
+
+void MapNodeSymbolGroupNode::debug(std::ostream &os, const std::string &visitingFullIname,
+ unsigned /* verbosity */, unsigned depth) const
+{
+ indentStream(os, 2 * depth);
+ os << "MapNode " << name() << '/' << visitingFullIname << '\n';
+}
+
// --------- DebugSymbolGroupNodeVisitor
// "local.vi" -> "local"
diff --git a/src/libs/qtcreatorcdbext/symbolgroupnode.h b/src/libs/qtcreatorcdbext/symbolgroupnode.h
index 88350741a2d92070f4d50a97b779ec088518edc2..dcb457c9eadbfed7537b8140ec7ecebdbe69a4e0 100644
--- a/src/libs/qtcreatorcdbext/symbolgroupnode.h
+++ b/src/libs/qtcreatorcdbext/symbolgroupnode.h
@@ -159,7 +159,8 @@ private:
void removeChildren();
};
-/* SymbolGroupNode: 'Real' node within a symbol group, identified by its index.
+/* SymbolGroupNode: 'Real' node within a symbol group, identified by its index
+ * in IDebugSymbolGroup.
* Provides accessors for fixed-up symbol group value and a dumping facility
* consisting of:
* - 'Simple' dumping done when running the DumpVisitor. This produces one
@@ -265,7 +266,7 @@ private:
// Artificial node referencing another (real) SymbolGroupNode (added symbol or
// symbol from within a linked list structure. Forwards dumping to referenced node
-// using its own name/iname.
+// using its own name.
class ReferenceSymbolGroupNode : public AbstractSymbolGroupNode
{
public:
@@ -290,6 +291,32 @@ private:
SymbolGroupNode * const m_referencedNode;
};
+// Base class for a [fake] map node with a fake array index and key/value entries.
+class MapNodeSymbolGroupNode : public BaseSymbolGroupNode
+{
+private:
+ explicit MapNodeSymbolGroupNode(const std::string &name,
+ const std::string &iname,
+ ULONG64 address /* = 0 */,
+ const std::string &type,
+ AbstractSymbolGroupNode *key,
+ AbstractSymbolGroupNode *value);
+
+public:
+ static MapNodeSymbolGroupNode *
+ create(int i, ULONG64 address /* = 0 */, const std::string &type,
+ SymbolGroupNode *key, SymbolGroupNode *value);
+
+ virtual void dump(std::ostream &str, const std::string &visitingFullIname,
+ const DumpParameters &p, const SymbolGroupValueContext &ctx);
+ virtual void debug(std::ostream &os, const std::string &visitingFullIname,
+ unsigned verbosity, unsigned depth) const;
+
+private:
+ const ULONG64 m_address;
+ const std::string m_type;
+};
+
/* Visitor that takes care of iterating over the nodes and
* building the full iname path ('local.foo.bar') that is required for
* GDBMI dumping. The full name depends on the path on which a node was reached
diff --git a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp
index 58e733637b1a52aea8c2a2c03fbc1b44510c8b62..c2513670a57aebbe9869437c1f072d4842c87f9e 100644
--- a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp
+++ b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp
@@ -189,15 +189,28 @@ SymbolGroupValue SymbolGroupValue::pointerTypeCast(const char *type) const
SymbolGroupValue SymbolGroupValue::typeCastedValue(ULONG64 address, const char *type) const
{
- if (address) {
- SymbolGroup *sg = m_node->symbolGroup();
- std::ostringstream str;
- str << '(' << type << ")(" << std::showbase << std::hex << address << ')';
- if (SymbolGroupNode *node = sg->addSymbol(str.str(),
- additionalSymbolIname(sg),
- &m_errorMessage))
- return SymbolGroupValue(node, m_context);
- }
+ if (!address)
+ return SymbolGroupValue();
+ const size_t len = strlen(type);
+ if (!len)
+ return SymbolGroupValue();
+ const bool nonPointer = type[len - 1] != '*';
+ SymbolGroup *sg = m_node->symbolGroup();
+ // A bit of magic: For desired pointer types, we can do
+ // 'Foo *' -> '(Foo *)(address)'.
+ // For non-pointers, we need to de-reference:
+ // 'Foo' -> '*(Foo *)(address)'
+ std::ostringstream str;
+ if (nonPointer)
+ str << '*';
+ str << '(' << type;
+ if (nonPointer)
+ str << " *";
+ str << ")(" << std::showbase << std::hex << address << ')';
+ if (SymbolGroupNode *node = sg->addSymbol(str.str(),
+ additionalSymbolIname(sg),
+ &m_errorMessage))
+ return SymbolGroupValue(node, m_context);
return SymbolGroupValue();
}