containers.cpp 44.9 KB
Newer Older
1 2 3 4
/**************************************************************************
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
8 9 10 11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28 29
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
30 31 32 33 34 35 36 37
**
**************************************************************************/

#include "containers.h"
#include "symbolgroupvalue.h"
#include "symbolgroup.h"
#include "stringutils.h"

38
#include <functional>
39
#include <iterator>
40

41
typedef AbstractSymbolGroupNode::AbstractSymbolGroupNodePtrVector AbstractSymbolGroupNodePtrVector;
42 43
typedef std::vector<SymbolGroupValue> SymbolGroupValueVector;
typedef std::vector<int>::size_type VectorIndexType;
44

45 46
// Read a pointer array from debuggee memory (ULONG64/32 according pointer size)
static void *readPointerArray(ULONG64 address, unsigned count, const SymbolGroupValueContext &ctx)
47
{
48 49 50 51 52 53 54 55 56 57
    const unsigned pointerSize = SymbolGroupValue::pointerSize();
    const ULONG allocSize = pointerSize * count;
    ULONG bytesRead = 0;
    void *data = new unsigned char[allocSize];
    const HRESULT hr = ctx.dataspaces->ReadVirtual(address, data, allocSize, &bytesRead);
    if (FAILED(hr) || bytesRead != allocSize) {
        delete [] data;
        return 0;
    }
    return data;
58 59
}

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
template <class UInt>
inline void dumpHexArray(std::ostream &os, const UInt *a, int count)
{
    os << std::showbase << std::hex;
    std::copy(a, a + count, std::ostream_iterator<UInt>(os, ", "));
    os << std::noshowbase << std::dec;
}

static inline void dump32bitPointerArray(std::ostream &os, const void *a, int count)
{
    dumpHexArray(os, reinterpret_cast<const ULONG32 *>(a), count);
}

static inline void dump64bitPointerArray(std::ostream &os, const void *a, int count)
{
    dumpHexArray(os, reinterpret_cast<const ULONG64 *>(a), count);
}

78 79
// Fix the inner type of containers (that is, make it work smoothly with AddSymbol)
// by prefixing it with the module except for well-known types like STL/Qt types
80 81
static inline std::string fixInnerType(std::string type,
                                       const SymbolGroupValue &container)
82
{
83 84
    const std::string stripped
        = SymbolGroupValue::stripConst(SymbolGroupValue::stripClassPrefixes(type));
85 86 87 88
    const KnownType kt = knownType(stripped, 0);
    // Resolve types unless they are POD or pointers to POD (that is, qualify 'Foo' and 'Foo*')
    const bool needResolve = kt == KT_Unknown || kt ==  KT_PointerType || !(kt & KT_POD_Type);
    const std::string fixed = needResolve ?
89
                SymbolGroupValue::resolveType(stripped, container.context(), container.module()) :
90 91 92 93 94 95 96 97
                stripped;
    if (SymbolGroupValue::verbose) {
        DebugPrint dp;
        dp << "fixInnerType (resolved=" << needResolve << ") '" << type << "' [";
        formatKnownTypeFlags(dp, kt);
        dp << "] -> '" << fixed <<"'\n";
    }
    return fixed;
98 99
}

100 101 102 103 104 105 106 107 108 109 110 111 112
// Return size from an STL vector (last/first iterators).
static inline int msvcStdVectorSize(const SymbolGroupValue &v)
{
    if (const SymbolGroupValue myFirstPtrV = v["_Myfirst"]) {
        if (const SymbolGroupValue myLastPtrV = v["_Mylast"]) {
            const ULONG64 firstPtr = myFirstPtrV.pointerValue();
            const ULONG64 lastPtr = myLastPtrV.pointerValue();
            if (!firstPtr || lastPtr < firstPtr)
                return -1;
            if (lastPtr == firstPtr)
                return 0;
            // Subtract the pointers: We need to do the pointer arithmetics ourselves
            // as we get char *pointers.
113
            const std::string innerType = fixInnerType(SymbolGroupValue::stripPointerType(myFirstPtrV.type()), v);
114 115 116 117 118 119 120 121 122
            const size_t size = SymbolGroupValue::sizeOf(innerType.c_str());
            if (size == 0)
                return -1;
            return static_cast<int>((lastPtr - firstPtr) / size);
        }
    }
    return -1;
}

123 124 125 126 127 128 129 130 131 132 133
// Return size of container or -1
int containerSize(KnownType kt, SymbolGroupNode *n, const SymbolGroupValueContext &ctx)
{
    QTC_TRACE_IN
    if ((kt & KT_ContainerType) == 0)
        return -1;
    const int ct = containerSize(kt, SymbolGroupValue(n, ctx));
    QTC_TRACE_OUT
    return ct;
}

134
/*! Determine size of containers \ingroup qtcreatorcdbext */
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
int containerSize(KnownType kt, const SymbolGroupValue &v)
{
    switch (kt) {
    case KT_QStringList:
        if (const SymbolGroupValue base = v[unsigned(0)])
            return containerSize(KT_QList, base);
        break;
    case KT_QList:
        if (const SymbolGroupValue dV = v["d"]) {
            if (const SymbolGroupValue beginV = dV["begin"]) {
                const int begin = beginV.intValue();
                const int end = dV["end"].intValue();
                if (begin >= 0 && end >= begin)
                    return end - begin;
            }
        }
        break;
    case KT_QLinkedList:
    case KT_QHash:
    case KT_QMap:
    case KT_QVector:
        if (const SymbolGroupValue sizeV = v["d"]["size"])
            return sizeV.intValue();
        break;
159 160 161 162
    case KT_QMultiHash:
        if (const SymbolGroupValue qHash = v[unsigned(0)])
            return containerSize(KT_QHash, qHash);
        break;
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
    case KT_QQueue:
        if (const SymbolGroupValue qList= v[unsigned(0)])
            return containerSize(KT_QList, qList);
        break;
    case KT_QStack:
        if (const SymbolGroupValue qVector = v[unsigned(0)])
            return containerSize(KT_QVector, qVector);
        break;
    case KT_QSet:
        if (const SymbolGroupValue base = v[unsigned(0)])
            return containerSize(KT_QHash, base);
        break;
    case KT_QMultiMap:
        if (const SymbolGroupValue base = v[unsigned(0)])
            return containerSize(KT_QMap, base);
        break;
    case KT_StdVector: {
        if (const SymbolGroupValue base = v[unsigned(0)]) {
            const int msvc10Size = msvcStdVectorSize(base);
            if (msvc10Size >= 0)
                return msvc10Size;
        }
        const int msvc8Size = msvcStdVectorSize(v);
        if (msvc8Size >= 0)
            return msvc8Size;
    }
        break;
    case KT_StdList:
        if (const SymbolGroupValue sizeV =  v["_Mysize"]) // VS 8
            return sizeV.intValue();
        if (const SymbolGroupValue sizeV = v[unsigned(0)][unsigned(0)]["_Mysize"]) // VS10
            return sizeV.intValue();
        break;
196 197 198 199 200 201 202 203
    case KT_StdDeque: {
        const SymbolGroupValue msvc10sizeV =  v[unsigned(0)]["_Mysize"]; // VS10
        if (msvc10sizeV)
            return msvc10sizeV.intValue();
        const SymbolGroupValue msvc8sizeV =  v["_Mysize"]; // VS8
        if (msvc8sizeV)
            return msvc8sizeV.intValue();
    }
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
        break;
    case KT_StdStack:
        if (const SymbolGroupValue deque =  v[unsigned(0)])
            return containerSize(KT_StdDeque, deque);
        break;
    case KT_StdSet:
    case KT_StdMap:
    case KT_StdMultiMap:
        if (const SymbolGroupValue baseV = v[unsigned(0)]) {
            if (const SymbolGroupValue sizeV = baseV["_Mysize"]) // VS 8
                return sizeV.intValue();
            if (const SymbolGroupValue sizeV = baseV[unsigned(0)][unsigned(0)]["_Mysize"]) // VS 10
                return sizeV.intValue();
        }
        break;
    }
    return -1;
}

/* Generate a list of children by invoking the functions to obtain the value
 * and the next link */
template <class ValueFunction, class NextFunction>
226 227 228 229
AbstractSymbolGroupNodePtrVector linkedListChildList(SymbolGroupValue headNode,
                                                     int count,
                                                     ValueFunction valueFunc,
                                                     NextFunction nextFunc)
230
{
231
    AbstractSymbolGroupNodePtrVector rc;
232 233 234
    rc.reserve(count);
    for (int i =0; i < count && headNode; i++) {
        if (const SymbolGroupValue value = valueFunc(headNode)) {
235
            rc.push_back(ReferenceSymbolGroupNode::createArrayNode(i, value.node()));
236 237 238 239 240 241 242 243 244
            headNode = nextFunc(headNode);
        } else {
            break;
        }
    }
    return rc;
}

// Helper function for linkedListChildList that returns a member by name
245 246
class MemberByName : public std::unary_function<const SymbolGroupValue &, SymbolGroupValue>
{
247 248 249 250 251 252 253 254 255
public:
    explicit MemberByName(const char *name) : m_name(name) {}
    SymbolGroupValue operator()(const SymbolGroupValue &v) { return v[m_name]; }

private:
    const char *m_name;
};

// std::list<T>: Dummy head node and then a linked list of "_Next", "_Myval".
256
static inline AbstractSymbolGroupNodePtrVector stdListChildList(SymbolGroupNode *n, int count,
257 258
                                                        const SymbolGroupValueContext &ctx)
{
259 260 261 262 263 264 265 266 267
    if (!count)
        return AbstractSymbolGroupNodePtrVector();
    const SymbolGroupValue head = SymbolGroupValue(n, ctx)[unsigned(0)][unsigned(0)]["_Myhead"]["_Next"];
    if (!head) {
        if (SymbolGroupValue::verbose)
            DebugPrint() << "std::list failure: " << head;
        return AbstractSymbolGroupNodePtrVector();
    }
    return linkedListChildList(head, count, MemberByName("_Myval"), MemberByName("_Next"));
268 269 270
}

// QLinkedList<T>: Dummy head node and then a linked list of "n", "t".
271
static inline AbstractSymbolGroupNodePtrVector qLinkedListChildList(SymbolGroupNode *n, int count,
272 273 274 275 276
                                                        const SymbolGroupValueContext &ctx)
{
    if (count)
        if (const SymbolGroupValue head = SymbolGroupValue(n, ctx)["e"]["n"])
            return linkedListChildList(head, count, MemberByName("t"), MemberByName("n"));
277
    return AbstractSymbolGroupNodePtrVector();
278 279
}

280
/* Helper for array-type containers:
281 282
 * Add a series of "*(innertype *)0x (address + n * size)" fake child symbols.
 * for a function generating a sequence of addresses. */
283

284
template <class AddressFunc>
285
AbstractSymbolGroupNodePtrVector arrayChildList(SymbolGroup *sg, AddressFunc addressFunc,
286 287 288
                                                const std::string &module,
                                                const std::string &innerType,
                                                int count)
289
{
290
    AbstractSymbolGroupNodePtrVector rc;
291
    if (!count)
292 293 294
        return rc;
    std::string errorMessage;
    rc.reserve(count);
295
    for (int i = 0; i < count; i++) {
296
        const std::string name = SymbolGroupValue::pointedToSymbolName(addressFunc(), innerType);
297
        if (SymbolGroupNode *child = sg->addSymbol(module, name, std::string(), &errorMessage)) {
298
            rc.push_back(ReferenceSymbolGroupNode::createArrayNode(i, child));
299
        } else {
300 301
            if (SymbolGroupValue::verbose)
                DebugPrint() << "addSymbol fails in arrayChildList";
302 303 304
            break;
        }
    }
305 306 307
    if (SymbolGroupValue::verbose)
        DebugPrint() << "arrayChildList '" << innerType << "' count=" << count << " returns "
                     << rc.size() << " elements";
308 309 310
    return rc;
}

311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
// Helper function for arrayChildList() taking a reference to an address and simply generating
// a sequence of address, address + delta, address + 2 * delta...
class AddressSequence
{
public:
    explicit inline AddressSequence(ULONG64 &address, ULONG delta) : m_address(address), m_delta(delta) {}
    inline ULONG64 operator()()
    {
        const ULONG64 rc = m_address;
        m_address += m_delta;
        return rc;
    }

private:
    ULONG64 &m_address;
    const ULONG m_delta;
};

329 330 331
static inline AbstractSymbolGroupNodePtrVector
    arrayChildList(SymbolGroup *sg, ULONG64 address, const std::string &module,
                   const std::string &innerType, int count)
332 333
{
    if (const unsigned innerTypeSize = SymbolGroupValue::sizeOf(innerType.c_str()))
334
        return arrayChildList(sg, AddressSequence(address, innerTypeSize),
335
                              module, innerType, count);
336
    return AbstractSymbolGroupNodePtrVector();
337 338
}

339
// std::vector<T>
340
static inline AbstractSymbolGroupNodePtrVector
341 342 343 344 345 346 347 348 349
    stdVectorChildList(SymbolGroupNode *n, int count, const SymbolGroupValueContext &ctx)
{
    if (count) {
        // std::vector<T>: _Myfirst is a pointer of T*. Get address
        // element to obtain address.
        const SymbolGroupValue vec(n, ctx);
        SymbolGroupValue myFirst = vec[unsigned(0)]["_Myfirst"]; // MSVC2010
        if (!myFirst)
            myFirst = vec["_Myfirst"]; // MSVC2008
350 351 352
        if (myFirst) {
            if (const ULONG64 address = myFirst.pointerValue()) {
                const std::string firstType = myFirst.type();
353 354
                const std::string innerType = fixInnerType(SymbolGroupValue::stripPointerType(firstType), vec);
                if (SymbolGroupValue::verbose)
355
                    DebugPrint() << n->name() << " inner type: '" << innerType << "' from '" << firstType << '\'';
356
                return arrayChildList(n->symbolGroup(), address, n->module(), innerType, count);
357 358
            }
        }
359
    }
360
    return AbstractSymbolGroupNodePtrVector();
361 362
}

363 364 365 366 367
// Helper for std::deque<>: From the array of deque blocks, read out the values.
template<class AddressType>
AbstractSymbolGroupNodePtrVector
    stdDequeChildrenHelper(SymbolGroup *sg,
                           const AddressType *blockArray, ULONG64 blockArraySize,
368
                           const std::string &module,
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
                           const std::string &innerType, ULONG64 innerTypeSize,
                           ULONG64 startOffset, ULONG64 dequeSize, int count)
{
    AbstractSymbolGroupNodePtrVector rc;
    rc.reserve(count);
    std::string errorMessage;
    // Determine block number and offset in the block array T[][dequeSize]
    // and create symbol by address.
    for (int i = 0; i < count; i++) {
        // see <deque>-header: std::deque<T>::iterator::operator*
        const ULONG64 offset = startOffset + i;
        ULONG64 block = offset / dequeSize;
        if (block >= blockArraySize)
            block -= blockArraySize;
        const ULONG64 blockOffset = offset % dequeSize;
        const ULONG64 address = blockArray[block] + innerTypeSize * blockOffset;
385
        if (SymbolGroupNode *n = sg->addSymbol(module, SymbolGroupValue::pointedToSymbolName(address, innerType), std::string(), &errorMessage)) {
386 387 388 389 390 391 392 393 394 395
            rc.push_back(ReferenceSymbolGroupNode::createArrayNode(i, n));
        } else {
            return AbstractSymbolGroupNodePtrVector();
        }
    }
    return rc;
}

// std::deque<>
static inline AbstractSymbolGroupNodePtrVector
396
    stdDequeDirectChildList(const SymbolGroupValue &deque, int count)
397 398 399
{
    if (!count)
        return AbstractSymbolGroupNodePtrVector();
400 401 402 403
    // From MSVC10 on, there is an additional base class
    const ULONG64 arrayAddress = deque["_Map"].pointerValue();
    const int startOffset = deque["_Myoff"].intValue();
    const int mapSize  = deque["_Mapsize"].intValue();
404 405
    if (!arrayAddress || startOffset < 0 || mapSize <= 0)
        return AbstractSymbolGroupNodePtrVector();
406
    const std::vector<std::string> innerTypes = deque.innerTypes();
407 408
    if (innerTypes.empty())
        return AbstractSymbolGroupNodePtrVector();
409
    const std::string innerType = fixInnerType(innerTypes.front(), deque);
410 411
    // Get the deque size (block size) which is an unavailable static member
    // (cf <deque> for the actual expression).
412
    const unsigned innerTypeSize = SymbolGroupValue::sizeOf(innerType.c_str());
413 414 415 416 417
    if (!innerTypeSize)
        return AbstractSymbolGroupNodePtrVector();
    const int dequeSize = innerTypeSize <= 1 ? 16 : innerTypeSize <= 2 ?
                               8 : innerTypeSize <= 4 ? 4 : innerTypeSize <= 8 ? 2 : 1;
    // Read out map array (pointing to the blocks)
418
    void *mapArray = readPointerArray(arrayAddress, mapSize, deque.context());
419 420 421
    if (!mapArray)
        return AbstractSymbolGroupNodePtrVector();
    const AbstractSymbolGroupNodePtrVector rc = SymbolGroupValue::pointerSize() == 8 ?
422
        stdDequeChildrenHelper(deque.node()->symbolGroup(),
423
                               reinterpret_cast<const ULONG64 *>(mapArray), mapSize,
424
                               deque.module(), innerType, innerTypeSize, startOffset, dequeSize, count) :
425
        stdDequeChildrenHelper(deque.node()->symbolGroup(),
426
                               reinterpret_cast<const ULONG32 *>(mapArray), mapSize,
427
                               deque.module(), innerType, innerTypeSize, startOffset, dequeSize, count);
428 429 430 431
    delete [] mapArray;
    return rc;
}

432 433 434 435 436 437 438 439 440
// std::deque<>
static inline AbstractSymbolGroupNodePtrVector
    stdDequeChildList(const SymbolGroupValue &v, int count)
{
    // MSVC10 has a base class. If that fails, try direct (MSVC2008)
    const AbstractSymbolGroupNodePtrVector msvc10rc = stdDequeDirectChildList(v[unsigned(0)], count);
    return msvc10rc.empty() ? stdDequeDirectChildList(v, count) : msvc10rc;
}

441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
/* Helper class for std::map<>,std::set<> based on std::__Tree:
 * We locally rebuild the structure in using instances of below class 'StdMapNode'
 * with 'left' and 'right' pointers and the values. Reason being that while it is
 * possible to write the iteration in terms of class SymbolGroupValue, it involves
 * going back up the tree over the flat node->parent pointers. Doing that in the debugger
 * sometimes ends up in nirvana, apparently due to it not being able to properly expand it.
 * StdMapNode has a buildMap() to build a hierarchy from a __Tree value,
 * begin() to return the first node and next() to iterate. The latter are modeled
 * after the _Tree::iterator base classes. (_Tree::begin, _Tree::iterator::operator++() */

class StdMapNode
{
private:
    StdMapNode(const StdMapNode &);
    StdMapNode &operator=(const StdMapNode &);

public:
    explicit StdMapNode(StdMapNode *p, const SymbolGroupValue &node, const SymbolGroupValue &value);
    ~StdMapNode() { delete m_left; delete m_right; }

    // Iterator helpers: Return first and move to next
    const StdMapNode *begin() const { return StdMapNode::leftMost(this); }
    static const StdMapNode *next(const StdMapNode *s);

    const SymbolGroupValue &value() const { return m_value; }

    // Build the hierarchy
    static StdMapNode *buildMap(const SymbolGroupValue &n);

    // Debug helpers
    void debug(std::ostream &os, unsigned depth = 0) const;

private:
    static StdMapNode *buildMapRecursion(const SymbolGroupValue &n, ULONG64 headAddress, StdMapNode *parent);
    static const StdMapNode *leftMost(const StdMapNode *n);

    StdMapNode *const m_parent;
    StdMapNode *m_left;
    StdMapNode *m_right;
    const SymbolGroupValue m_node;
    const SymbolGroupValue m_value;
};

StdMapNode::StdMapNode(StdMapNode *p, const SymbolGroupValue &n, const SymbolGroupValue &v) :
    m_parent(p), m_left(0), m_right(0), m_node(n), m_value(v)
{
}

const StdMapNode *StdMapNode::leftMost(const StdMapNode *n)
{
    for ( ; n->m_left ; n = n->m_left ) ;
    return n;
}

const StdMapNode *StdMapNode::next(const StdMapNode *s)
{
    if (s->m_right) // If we have a right node, return its left-most
        return StdMapNode::leftMost(s->m_right);
    do { // Climb looking for 'right' subtree, that is, we are left of it
        StdMapNode *parent = s->m_parent;
        if (!parent || parent->m_right != s)
            return parent;
        s = parent;
    } while (true);
    return 0;
}

StdMapNode *StdMapNode::buildMapRecursion(const SymbolGroupValue &n, ULONG64 headAddress, StdMapNode *parent)
{
    const SymbolGroupValue value = n["_Myval"];
    if (!value)
        return 0;
    StdMapNode *node = new StdMapNode(parent, n, value);
    // Get left and right nodes. A node pointing to head terminates the recursion
    if (const SymbolGroupValue left = n["_Left"])
        if (const ULONG64 leftAddr = left.pointerValue())
            if (leftAddr != headAddress)
                node->m_left = buildMapRecursion(left, headAddress, node);
    if (const SymbolGroupValue right = n["_Right"])
        if (const ULONG64 rightAddr = right.pointerValue())
            if (rightAddr != headAddress)
                node->m_right = buildMapRecursion(right, headAddress, node);
    return node;
}

StdMapNode *StdMapNode::buildMap(const SymbolGroupValue &n)
{
    // Goto root of tree (see _Tree::_Root())
    if (const SymbolGroupValue head = n["_Myhead"])
        if (const ULONG64 headAddress = head.pointerValue())
            return buildMapRecursion(head["_Parent"], headAddress, 0);
    return 0;
}

static inline void indentStream(std::ostream &os, unsigned indent)
{
Friedemann Kleint's avatar
Friedemann Kleint committed
537
    for (unsigned i = 0; i < indent; ++i)
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
        os << ' ';
}

// Debugging helper for a SymbolGroupValue containing a __Tree::node of
// a map (assuming a std::pair inside).
static inline void debugMSVC2010MapNode(const SymbolGroupValue &n, std::ostream &os, unsigned indent = 0)
{
    indentStream(os, indent);
    os << "Node at " << std::hex << std::showbase << n.address()
       << std::dec << std::noshowbase
       << " Value='" << wStringToString(n.value()) << "', Parent=" << wStringToString(n["_Parent"].value())
       << ", Left=" << wStringToString(n["_Left"].value())
       << ", Right=" << wStringToString(n["_Right"].value())
       << ", nil='" <<  wStringToString(n["_Isnil"].value());
    if (const SymbolGroupValue pairBase = n["_Myval"][unsigned(0)]) {
        os << "', key='"  << wStringToString(pairBase["first"].value())
           << "', value='"   << wStringToString(pairBase["second"].value())
           << '\'';
    } else {
        os << "', key='"  << wStringToString(n["_Myval"].value()) << '\'';
    }
    os << '\n';
}

void StdMapNode::debug(std::ostream &os, unsigned depth) const
{
    indentStream(os, 2 * depth);
    os << "StdNode=" << this << " Left=" << m_left  << " Right=" << m_right << '\n';
    debugMSVC2010MapNode(m_node, os, 2 * depth);
    if (m_left)
        m_left->debug(os, depth + 1);
    if (m_right)
        m_right->debug(os, depth + 1);
}

// Helper for std::map<>,std::set<> based on std::__Tree:
// Return the list of children (pair for maps, direct children for set)
static inline SymbolGroupValueVector
576
    stdTreeChildList(const SymbolGroupValue &tree, int count, bool *isMSVC2010In = 0)
577
{
578 579 580 581
    if (!count)
        return SymbolGroupValueVector();
    // MSVC2010: "class _Tree : public _Tree_val: public _Tree_nod".
    // MSVC2008: Direct class
582 583
    const int size = tree[unsigned(0)][unsigned(0)]["_Mysize"].intValue();
    const bool isMSVC2010 = size >= 0 && size <= count; // Count may be limited
584 585 586 587 588 589
    if (isMSVC2010In)
        *isMSVC2010In = isMSVC2010;
    const SymbolGroupValue treeNode = isMSVC2010 ? tree[unsigned(0)][unsigned(0)] : tree;
    if (!treeNode)
        return SymbolGroupValueVector();
    // Build the tree and iterate it.
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
    const StdMapNode *nodeTree = StdMapNode::buildMap(treeNode);
    if (!nodeTree)
        return SymbolGroupValueVector();
    SymbolGroupValueVector rc;
    rc.reserve(count);
    int i = 0;
    for (const StdMapNode *n = nodeTree->begin() ; n && i < count; n = StdMapNode::next(n), i++)
        rc.push_back(n->value());
    delete nodeTree;
    if (rc.size() != count)
        return SymbolGroupValueVector();
    return rc;
}

// std::set<>: Children directly contained in list
static inline AbstractSymbolGroupNodePtrVector
    stdSetChildList(const SymbolGroupValue &set, int count)
{
    const SymbolGroupValueVector children = stdTreeChildList(set[unsigned(0)], count);
    if (int(children.size()) != count)
        return AbstractSymbolGroupNodePtrVector();
    AbstractSymbolGroupNodePtrVector rc;
    rc.reserve(count);
    for (int i = 0; i < count; i++)
        rc.push_back(ReferenceSymbolGroupNode::createArrayNode(i, children.at(i).node()));
    return rc;
}

// std::map<K,V>: A list of std::pair<K,V> (derived from std::pair_base<K,V>)
static inline AbstractSymbolGroupNodePtrVector
    stdMapChildList(const SymbolGroupValue &map, int count)
{
    bool isMSVC2010 = true;
    const SymbolGroupValueVector children = stdTreeChildList(map[unsigned(0)], count, &isMSVC2010);
    if (int(children.size()) != count)
        return AbstractSymbolGroupNodePtrVector();
    AbstractSymbolGroupNodePtrVector rc;
    rc.reserve(count);
    for (int i = 0; i < count; i++) {
        // MSVC2010 introduces a std::pair_base.
        const SymbolGroupValue pairBase = isMSVC2010?
                    children.at(i)[unsigned(0)] : children.at(i);
        const SymbolGroupValue key = pairBase["first"];
        const SymbolGroupValue value = pairBase["second"];
        if (key && value) {
            rc.push_back(MapNodeSymbolGroupNode::create(i, pairBase.address(),
                                                        pairBase.type(),
                                                        key.node(), value.node()));
        } else {
            return AbstractSymbolGroupNodePtrVector();
        }
    }
    return rc;
}

645
// QVector<T>
646
static inline AbstractSymbolGroupNodePtrVector
647 648 649 650 651 652
    qVectorChildList(SymbolGroupNode *n, int count, const SymbolGroupValueContext &ctx)
{
    if (count) {
        // QVector<T>: p/array is declared as array of T. Dereference first
        // element to obtain address.
        const SymbolGroupValue vec(n, ctx);
653 654
        if (const SymbolGroupValue firstElementV = vec["p"]["array"][unsigned(0)]) {
            if (const ULONG64 arrayAddress = firstElementV.address()) {
655
                const std::string fixedInnerType = fixInnerType(firstElementV.type(), vec);
656
                return arrayChildList(n->symbolGroup(), arrayAddress, n->module(), fixedInnerType, count);
657 658
            }
        }
659
    }
660
    return AbstractSymbolGroupNodePtrVector();
661 662
}

663 664 665 666 667 668 669 670 671 672 673 674 675 676
// Helper function for arrayChildList() for use with QLists of large types that are an
// array of pointers to allocated elements: Generate a pointer sequence by reading out the array.
template <class AddressType>
class AddressArraySequence
{
public:
    explicit inline AddressArraySequence(const AddressType *array) : m_array(array) {}
    inline ULONG64 operator()() { return *m_array++; }

private:
    const AddressType *m_array;
};

// QList<>.
677
static inline AbstractSymbolGroupNodePtrVector
678
    qListChildList(const SymbolGroupValue &v, int count)
679 680 681
{
    // QList<T>: d/array is declared as array of void *[]. Dereference first
    // element to obtain address.
682
    if (!count)
683
        return AbstractSymbolGroupNodePtrVector();
684 685
    const SymbolGroupValue dV = v["d"];
    if (!dV)
686
        return AbstractSymbolGroupNodePtrVector();
687 688
    const int begin = dV["begin"].intValue();
    if (begin < 0)
689
        return AbstractSymbolGroupNodePtrVector();
690 691
    const SymbolGroupValue firstElementV = dV["array"][unsigned(0)];
    if (!firstElementV)
692
        return AbstractSymbolGroupNodePtrVector();
693
     ULONG64 arrayAddress = firstElementV.address();
694
     if (!arrayAddress)
695
         return AbstractSymbolGroupNodePtrVector();
696 697
     const std::vector<std::string> innerTypes = v.innerTypes();
     if (innerTypes.size() != 1)
698
         return AbstractSymbolGroupNodePtrVector();
699
     const std::string innerType = fixInnerType(innerTypes.front(), v);
700
     const unsigned innerTypeSize = SymbolGroupValue::sizeOf(innerType.c_str());
701
     if (SymbolGroupValue::verbose)
702
         DebugPrint() << "QList " << v.name() << " inner type " << innerType << ' ' << innerTypeSize;
703
     if (!innerTypeSize)
704
         return AbstractSymbolGroupNodePtrVector();
705 706 707 708 709 710 711 712 713
     /* QList<> is:
      * 1) An array of 'void *[]' where T values are coerced into the elements for
      *    POD/pointer types and small, movable or primitive Qt types. That is, smaller
      *    elements are also aligned at 'void *' boundaries.
      * 2) An array of 'T *[]' (pointer to allocated instances) for anything else
      *    (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic)
      *    isStatic depends on QTypeInfo specializations and hardcoded flags for types. */
     const unsigned pointerSize = SymbolGroupValue::pointerSize();
     arrayAddress += begin * pointerSize;
714 715
     if (SymbolGroupValue::isPointerType(innerType)) // Quick check: Any pointer is T[]
         return arrayChildList(v.node()->symbolGroup(),
716
                               AddressSequence(arrayAddress, pointerSize),
717
                               v.module(), innerType, count);
718
     // Check condition for large||static.
719
     bool isLargeOrStatic = innerTypeSize > pointerSize;
720
     if (!isLargeOrStatic && !SymbolGroupValue::isPointerType(innerType)) {
721
         const KnownType kt = knownType(innerType, false); // inner type, no 'class ' prefix.
722
         if (kt != KT_Unknown && !(kt & (KT_POD_Type|KT_Qt_PrimitiveType|KT_Qt_MovableType)))
723 724
             isLargeOrStatic = true;
     }
725
     if (SymbolGroupValue::verbose)
726
         DebugPrint() << "isLargeOrStatic " << isLargeOrStatic;
727 728
     if (isLargeOrStatic) {
         // Retrieve the pointer array ourselves to avoid having to evaluate '*(class foo**)'
729 730 731
         if (void *data = readPointerArray(arrayAddress, count, v.context()))  {
             // Generate sequence of addresses from pointer array
             const AbstractSymbolGroupNodePtrVector rc = pointerSize == 8 ?
732 733 734 735 736
                         arrayChildList(v.node()->symbolGroup(),
                                        AddressArraySequence<ULONG64>(reinterpret_cast<const ULONG64 *>(data)),
                                        v.module(), innerType, count) :
                         arrayChildList(v.node()->symbolGroup(), AddressArraySequence<ULONG32>(reinterpret_cast<const ULONG32 *>(data)),
                                        v.module(), innerType, count);
737
             delete [] data;
738
             return rc;
739
         }
740
         return AbstractSymbolGroupNodePtrVector();
741
     }
742
     return arrayChildList(v.node()->symbolGroup(),
743
                           AddressSequence(arrayAddress, pointerSize),
744
                           v.module(), innerType, count);
745 746
}

747 748 749
// Return the list of buckets of a 'QHash<>' as 'QHashData::Node *' values from
// the list of addresses passed in
template<class AddressType>
750
SymbolGroupValueVector hashBuckets(SymbolGroup *sg, const std::string &hashNodeType,
751 752 753
                                   const AddressType *pointerArray,
                                   int numBuckets,
                                   AddressType ePtr,
754
                                   const std::string &module,
755 756 757 758 759 760 761 762 763 764
                                   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) {
765
            const std::string name = SymbolGroupValue::pointedToSymbolName(*p, hashNodeType);
766
            if (SymbolGroupNode *child = sg->addSymbol(module, name, std::string(), &errorMessage)) {
767 768 769 770 771 772 773 774 775 776
                rc.push_back(SymbolGroupValue(child, ctx));
            } else {
                return std::vector<SymbolGroupValue>();
                break;
            }
        }
    }
    return rc;
}

777
// Return the node type of a QHash/QMap:
778
// "class QHash<K,V>[ *]" -> [struct] "QtCored4!QHashNode<K,V>";
779 780
static inline std::string qHashNodeType(const SymbolGroupValue &v,
                                        const char *nodeType)
781
{
782
    std::string qHashType = SymbolGroupValue::stripPointerType(v.type());
783 784
    const std::string::size_type pos = qHashType.find('<');
    if (pos != std::string::npos)
785 786 787 788 789
        qHashType.insert(pos, nodeType);
    // A map node must be qualified with the current module and
    // the Qt namespace (particularly QMapNode, QHashNodes work also for
    // the unqualified case).
    const QtInfo &qtInfo = QtInfo::get(v.context());
790
    const std::string currentModule = v.module();
791
    return QtInfo::prependModuleAndNameSpace(qHashType, currentModule, qtInfo.nameSpace);
792 793 794 795 796 797 798 799 800 801 802
}

// 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();
803
    if (SymbolGroupValue::verbose)
804
        DebugPrint() << v << " Count=" << count << ",ePtr=0x" << std::hex << ePtr;
805 806 807 808 809 810 811 812 813 814 815
    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')
Friedemann Kleint's avatar
Friedemann Kleint committed
816
    const std::string dummyNodeType = QtInfo::get(v.context()).prependQtCoreModule("QHashData::Node");
817 818 819
    const SymbolGroupValueVector buckets = SymbolGroupValue::pointerSize() == 8 ?
        hashBuckets(v.node()->symbolGroup(), dummyNodeType,
                    reinterpret_cast<const ULONG64 *>(bucketPointers), numBuckets,
820
                    ePtr, v.module(), v.context()) :
821 822
        hashBuckets(v.node()->symbolGroup(), dummyNodeType,
                    reinterpret_cast<const ULONG32 *>(bucketPointers), numBuckets,
823
                    ULONG32(ePtr), v.module(), v.context());
824 825 826 827
    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
828 829
    // dummy element. The initial element as such is skipped due to the pointer/value
    // duality (since its 'next' element is identical to it when using typecast<> later on).
830 831 832 833 834 835 836 837
    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
838
                dummyNodeList.push_back(next);
839 840
                if (dummyNodeList.size() >= count) // Stop at maximum count
                    notEnough = false;
841
                if (SymbolGroupValue::verbose > 1)
842
                    DebugPrint() << '#' << (dummyNodeList.size() - 1) << " l=" << l << ",next=" << next;
843
                l = next;
844 845 846 847 848 849
            } else {
                break;
            }
        }
    }
    // Finally convert them into real nodes 'QHashNode<K,V> (potentially expensive)
850
    const std::string nodeType = qHashNodeType(v, "Node");
851
    if (SymbolGroupValue::verbose)
852
        DebugPrint() << "Converting into " << nodeType;
853 854 855 856 857 858 859 860 861 862 863 864 865 866 867
    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.
868
static inline AbstractSymbolGroupNodePtrVector
869
    qSetChildList(const SymbolGroupValue &v, int count)
870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890
{
    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
891
    qHashChildList(const SymbolGroupValue &v, int count)
892 893 894 895
{
    AbstractSymbolGroupNodePtrVector rc;
    if (!count)
        return rc;
896 897 898 899 900 901 902 903 904 905 906 907 908
    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()));
    }
909 910 911
    return rc;
}

912 913 914 915 916
// QMap<>: Return the list of QMapData::Node
static inline SymbolGroupValueVector qMapNodes(const SymbolGroupValue &v, VectorIndexType count)
{
    const SymbolGroupValue e = v["e"];
    const ULONG64 ePtr = e.pointerValue();
917
    if (SymbolGroupValue::verbose)
918 919 920
        DebugPrint() << v.type() << " E=0x" << std::hex << ePtr;
    if (!ePtr)
        return SymbolGroupValueVector();
921
    if (SymbolGroupValue::verbose)
922 923 924 925
        DebugPrint() << v.type() << " E=0x" << std::hex << ePtr;
    SymbolGroupValueVector rc;
    rc.reserve(count);
    SymbolGroupValue n = e["forward"][unsigned(0)];
Friedemann Kleint's avatar
Friedemann Kleint committed
926
    for (VectorIndexType i = 0; i < count && n && n.pointerValue() != ePtr; ++i) {
927 928 929 930 931 932 933 934 935 936
        rc.push_back(n);
        n = n["forward"][unsigned(0)];
    }
    return rc;
}

// QMap<>: Add with fake map nodes.
static inline AbstractSymbolGroupNodePtrVector
    qMapChildList(const SymbolGroupValue &v, VectorIndexType count)
{
937
    if (SymbolGroupValue::verbose)
938 939 940 941 942 943 944 945 946 947 948
        DebugPrint() << v.type() << "," << count;

    if (!count)
        return AbstractSymbolGroupNodePtrVector();
    // Get node type: 'class namespace::QMap<K,T>'
    // ->'QtCored4!namespace::QMapNode<K,T>'
    // Note: Any types QMapNode<> will not be found without modules!
    const std::string mapNodeType = qHashNodeType(v, "Node");
    const std::string mapPayloadNodeType = qHashNodeType(v, "PayloadNode");
    // Calculate the offset needed (see QMap::concrete() used by the iterator).
    const unsigned payloadNodeSize = SymbolGroupValue::sizeOf(mapPayloadNodeType.c_str());
949
    if (SymbolGroupValue::verbose) {
950
        DebugPrint() << v.type() << "," << mapNodeType << ':'
951 952
                     << mapPayloadNodeType << ':' << payloadNodeSize
                     << ", pointerSize=" << SymbolGroupValue::pointerSize();
953
    }
954
    if (!payloadNodeSize)
955
        return AbstractSymbolGroupNodePtrVector();
956
    const ULONG64 payLoad  = payloadNodeSize - SymbolGroupValue::pointerSize();
957 958 959 960 961 962
    // Get the value offset. Manually determine the alignment to be able
    // to retrieve key/value without having to deal with QMapNode<> (see below).
    // Subtract the 2 trailing pointers of the node.
    const std::vector<std::string> innerTypes = v.innerTypes();
    if (innerTypes.size() != 2u)
        return AbstractSymbolGroupNodePtrVector();
963 964 965
    const std::string keyType = fixInnerType(innerTypes.front(), v);
    const std::string valueType = fixInnerType(innerTypes.at(1), v);
    const unsigned valueSize = SymbolGroupValue::sizeOf(valueType.c_str());
966
    const unsigned valueOffset = SymbolGroupValue::fieldOffset(mapNodeType.c_str(), "value");
967
    if (SymbolGroupValue::verbose)
968 969 970 971 972 973
        DebugPrint() << "Payload=" << payLoad << ",valueOffset=" << valueOffset << ','
                     << innerTypes.front() << ',' << innerTypes.back() << ':' << valueSize;
    if (!valueOffset || !valueSize)
        return AbstractSymbolGroupNodePtrVector();
    // Get the children.
    const SymbolGroupValueVector childNodes = qMapNodes(v, count);
974
    if (SymbolGroupValue::verbose)
975 976 977 978 979 980 981 982 983 984 985 986 987 988
        DebugPrint() << "children: " << childNodes.size() << " of " << count;
    // Deep  expansion of the forward[0] sometimes fails. In that case,
    // take what we can get.
    if (childNodes.size() != count)
        count = childNodes.size();
    // The correct way of doing this would be to construct additional symbols
    // '*(QMapNode<K,V> *)(node_address)'. However, when doing this as of
    // 'CDB 6.12.0002.633' (21.12.2010) IDebugSymbolGroup::AddSymbol()
    // just fails, returning DEBUG_ANY_ID without actually doing something. So,
    // we circumvent the map nodes and directly create key and values at their addresses.
    AbstractSymbolGroupNodePtrVector rc;
    rc.reserve(count);
    std::string errorMessage;
    SymbolGroup *sg = v.node()->symbolGroup();
Friedemann Kleint's avatar
Friedemann Kleint committed
989
    for (VectorIndexType i = 0; i < count ; ++i) {
990 991 992 993
        const ULONG64 nodePtr = childNodes.at(i).pointerValue();
        if (!nodePtr)
            return AbstractSymbolGroupNodePtrVector();
        const ULONG64 keyAddress = nodePtr - payLoad;
994 995
        const std::string keyExp = SymbolGroupValue::pointedToSymbolName(keyAddress, keyType);
        const std::string valueExp = SymbolGroupValue::pointedToSymbolName(keyAddress + valueOffset, valueType);
996
        if (SymbolGroupValue::verbose) {
997 998 999 1000
            DebugPrint() << '#' << i << '/' << count << ' ' << std::hex << ",node=0x" << nodePtr <<
                  ',' <<keyExp << ',' << valueExp;
        }
        // Create the nodes
1001 1002
        SymbolGroupNode *keyNode = sg->addSymbol(v.module(), keyExp, std::string(), &errorMessage);
        SymbolGroupNode *valueNode = sg->addSymbol(v.module(), valueExp, std::string(), &errorMessage);
1003 1004 1005 1006 1007 1008 1009 1010
        if (!keyNode || !valueNode)
            return AbstractSymbolGroupNodePtrVector();
        rc.push_back(MapNodeSymbolGroupNode::create(int(i), keyAddress,
                                                    mapNodeType, keyNode, valueNode));
    }
    return rc;
}

1011
/*! Determine children of containers \ingroup qtcreatorcdbext */
1012 1013
AbstractSymbolGroupNodePtrVector containerChildren(SymbolGroupNode *node, int type,
                                                   int size, const SymbolGroupValueContext &ctx)
1014
{
1015 1016 1017 1018 1019 1020 1021 1022
    if (SymbolGroupValue::verbose) {
        DebugPrint dp;
        dp << "containerChildren " << node->name() << '/' << node->iName() << '/' << node->type()
           << " at 0x" << std::hex << node->address() << std::dec
           << " count=" << size << ",knowntype=" << type << " [";
        formatKnownTypeFlags(dp, static_cast<KnownType>(type));
        dp << ']';
    }
1023
    if (!size)
1024
        return AbstractSymbolGroupNodePtrVector();
1025 1026 1027 1028 1029 1030 1031
    if (size > 100)
        size = 100;
    switch (type) {
    case KT_QVector:
        return qVectorChildList(node, size, ctx);
    case KT_StdVector:
        return stdVectorChildList(node, size, ctx);
1032 1033
    case KT_QLinkedList:
        return qLinkedListChildList(node, size, ctx);
1034
    case KT_QList:
1035 1036 1037 1038 1039 1040 1041 1042
        return qListChildList(SymbolGroupValue(node, ctx), size);
    case KT_QQueue:
        if (const SymbolGroupValue qList = SymbolGroupValue(node, ctx)[unsigned(0)])
            return qListChildList(qList, size);
        break;
    case KT_QStack:
        if (const SymbolGroupValue qVector = SymbolGroupValue(node, ctx)[unsigned(0)])
            return qVectorChildList(qVector.node(), size, ctx);
1043
        break;
1044 1045
    case KT_QHash:
        return qHashChildList(SymbolGroupValue(node, ctx), size);
1046
    case KT_QMultiHash:
1047 1048 1049 1050
        if (const SymbolGroupValue hash = SymbolGroupValue(node, ctx)[unsigned(0)])
            return qHashChildList(hash, size);
        break;
    case KT_QSet:
1051
        return qSetChildList(SymbolGroupValue(node, ctx), size);
1052 1053 1054 1055 1056 1057
    case KT_QMap:
        return qMapChildList(SymbolGroupValue(node, ctx), size);
    case KT_QMultiMap:
        if (const SymbolGroupValue qmap = SymbolGroupValue(node, ctx)[unsigned(0)])
            return qMapChildList(qmap, size);
        break;
1058 1059
    case KT_QStringList:
        if (const SymbolGroupValue qList = SymbolGroupValue(node, ctx)[unsigned(0)])
1060
            return qListChildList(qList, size);
1061
        break;