From edeccf73077bd799c8636c0e04d468e6f5467be5 Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Date: Fri, 8 Oct 2010 14:55:57 +0200
Subject: [PATCH] Debugger: Fix dumpers in case alphabetical sorting is off.

No longer change iname to obtain sorting.

Reviewed-by: hjk
---
 .../debugger/cdb/cdbsymbolgroupcontext.cpp    | 16 ++--
 .../debugger/cdb/cdbsymbolgroupcontext_tpl.h  |  2 +
 src/plugins/debugger/gdb/classicgdbengine.cpp |  5 +-
 src/plugins/debugger/gdb/pythongdbengine.cpp  |  4 +-
 src/plugins/debugger/pdb/pdbengine.cpp        |  3 +-
 src/plugins/debugger/watchdata.cpp            |  4 +-
 src/plugins/debugger/watchdata.h              |  1 +
 src/plugins/debugger/watchhandler.cpp         | 80 ++++++++++++-------
 src/plugins/debugger/watchutils.cpp           | 11 +--
 src/plugins/debugger/watchutils.h             |  2 +-
 10 files changed, 73 insertions(+), 55 deletions(-)

diff --git a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp
index fae1a564244..9cd8f6903cf 100644
--- a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp
+++ b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp
@@ -187,15 +187,15 @@ static inline void fixDumperResult(const WatchData &source,
     // If the model queries the expanding item by pretending childrenNeeded=1,
     // refuse the request as the children are already known
     returned.source |= CdbSymbolGroupContext::ChildrenKnownBit;
-    // Fix the children: If the address is missing, we cannot query any further.
-    const QList<WatchData>::iterator wend = result->end();
-    QList<WatchData>::iterator it = result->begin();
-    for (++it; it != wend; ++it) {
-        WatchData &wd = *it;
+    // Fix the children: Assign sort id , if the address is missing, we cannot query any further.
+    const int resultSize = result->size();
+    for (int i = 1; i < resultSize; i++) {
+        WatchData &wd = (*result)[i];
+        wd.sortId = i;
         // Indicate owner and known children
-        it->source = OwnerDumper;
-        if (it->isChildrenKnown() && it->isHasChildrenKnown() && it->hasChildren)
-            it->source |= CdbSymbolGroupContext::ChildrenKnownBit;
+        wd.source = OwnerDumper;
+        if (wd.isChildrenKnown() && wd.isHasChildrenKnown() && wd.hasChildren)
+            wd.source |= CdbSymbolGroupContext::ChildrenKnownBit;
         // Cannot dump items with missing addresses or missing types
         const bool typeFixed = fixDumperType(&wd); // Order of evaluation!
         if ((wd.address == 0 && wd.isSomethingNeeded()) || typeFixed) {
diff --git a/src/plugins/debugger/cdb/cdbsymbolgroupcontext_tpl.h b/src/plugins/debugger/cdb/cdbsymbolgroupcontext_tpl.h
index 9cc79b3eecf..fcaf098bb76 100644
--- a/src/plugins/debugger/cdb/cdbsymbolgroupcontext_tpl.h
+++ b/src/plugins/debugger/cdb/cdbsymbolgroupcontext_tpl.h
@@ -52,6 +52,7 @@ bool CdbSymbolGroupContext::getDumpChildSymbols(const QString &prefix,
     // children, so, re-evaluate size in end condition.
     // Note the that the internal dumpers might expand children,
     // so the size might change.
+    int sortId = 0;
     for (int s = start; s < size(); ++s) {
         const DEBUG_SYMBOL_PARAMETERS &p = symbolParameterAt(s);
         if (p.ParentSymbol == parentId && isSymbolDisplayable(p)) {
@@ -59,6 +60,7 @@ bool CdbSymbolGroupContext::getDumpChildSymbols(const QString &prefix,
             const unsigned rc = watchDataAt(s, &wd);
             if (rc & InternalDumperMask)
                 wd.source = dumpedOwner;
+            wd.sortId = sortId++;
             *it = wd;
             ++it;
         }
diff --git a/src/plugins/debugger/gdb/classicgdbengine.cpp b/src/plugins/debugger/gdb/classicgdbengine.cpp
index 6918f23c20f..090c4cbd953 100644
--- a/src/plugins/debugger/gdb/classicgdbengine.cpp
+++ b/src/plugins/debugger/gdb/classicgdbengine.cpp
@@ -425,10 +425,7 @@ void GdbEngine::handleDebuggingHelperValue2Classic(const GdbResponse &response)
     setWatchDataType(data, response.data.findChild("type"));
     setWatchDataDisplayedType(data, response.data.findChild("displaytype"));
     QList<WatchData> list;
-    parseWatchData(watchHandler()->expandedINames(),
-        data, contents,
-        theDebuggerBoolSetting(SortStructMembers),
-        &list);
+    parseWatchData(watchHandler()->expandedINames(), data, contents, &list);
     //for (int i = 0; i != list.size(); ++i)
     //    qDebug() << "READ: " << list.at(i).toString();
     watchHandler()->insertBulkData(list);
diff --git a/src/plugins/debugger/gdb/pythongdbengine.cpp b/src/plugins/debugger/gdb/pythongdbengine.cpp
index 20e06886985..b55c5e5d25a 100644
--- a/src/plugins/debugger/gdb/pythongdbengine.cpp
+++ b/src/plugins/debugger/gdb/pythongdbengine.cpp
@@ -133,14 +133,12 @@ void GdbEngine::handleStackFramePython(const GdbResponse &response)
 
         GdbMi data = all.findChild("data");
         QList<WatchData> list;
-        const bool sortMembers = theDebuggerBoolSetting(SortStructMembers);
         foreach (const GdbMi &child, data.children()) {
             WatchData dummy;
             dummy.iname = child.findChild("iname").data();
             dummy.name = _(child.findChild("name").data());
             //qDebug() << "CHILD: " << child.toString();
-            parseWatchData(watchHandler()->expandedINames(), dummy, child,
-                sortMembers, &list);
+            parseWatchData(watchHandler()->expandedINames(), dummy, child, &list);
         }
         watchHandler()->insertBulkData(list);
         //for (int i = 0; i != list.size(); ++i)
diff --git a/src/plugins/debugger/pdb/pdbengine.cpp b/src/plugins/debugger/pdb/pdbengine.cpp
index dd55400e008..3c857fbf461 100644
--- a/src/plugins/debugger/pdb/pdbengine.cpp
+++ b/src/plugins/debugger/pdb/pdbengine.cpp
@@ -826,13 +826,12 @@ void PdbEngine::handleListLocals(const PdbResponse &response)
     //GdbMi data = all.findChild("data");
     QList<WatchData> list;
     WatchHandler *handler = watchHandler();
-    bool sortMembers = theDebuggerBoolSetting(SortStructMembers);
     foreach (const GdbMi &child, all.children()) {
         WatchData dummy;
         dummy.iname = child.findChild("iname").data();
         dummy.name = _(child.findChild("name").data());
         //qDebug() << "CHILD: " << child.toString();
-        parseWatchData(handler->expandedINames(), dummy, child, sortMembers, &list);
+        parseWatchData(handler->expandedINames(), dummy, child, &list);
     }
     handler->insertBulkData(list);
 }
diff --git a/src/plugins/debugger/watchdata.cpp b/src/plugins/debugger/watchdata.cpp
index 74171d0d0b8..b21d1609f6a 100644
--- a/src/plugins/debugger/watchdata.cpp
+++ b/src/plugins/debugger/watchdata.cpp
@@ -28,7 +28,8 @@ WatchData::WatchData() :
     source(0),
     objectId(0),
     state(InitialState),
-    changed(false)
+    changed(false),
+    sortId(0)
 {
 }
 
@@ -172,6 +173,7 @@ QString WatchData::toString() const
     str << QLatin1Char('{');
     if (!iname.isEmpty())
         str << "iname=\"" << iname << doubleQuoteComma;
+    str << "sortId=\"" << sortId << doubleQuoteComma;
     if (!name.isEmpty() && name != iname)
         str << "name=\"" << name << doubleQuoteComma;
     if (error)
diff --git a/src/plugins/debugger/watchdata.h b/src/plugins/debugger/watchdata.h
index c64473767ae..f36fce1fe65 100644
--- a/src/plugins/debugger/watchdata.h
+++ b/src/plugins/debugger/watchdata.h
@@ -136,6 +136,7 @@ public:
     quint64 objectId; // Object id used for the QMLEngine
     int state;
     bool changed;
+    int sortId;
 };
 
 } // namespace Internal
diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp
index fd428f211ef..1c5b6cdad97 100644
--- a/src/plugins/debugger/watchhandler.cpp
+++ b/src/plugins/debugger/watchhandler.cpp
@@ -960,40 +960,62 @@ QVariant WatchModel::headerData(int section, Qt::Orientation orientation, int ro
     return QVariant();
 }
 
-struct IName : public QByteArray
-{
-    IName(const QByteArray &iname) : QByteArray(iname) {}
-};
+// Determine sort order of watch items by sort order or alphabetical inames
+// according to setting 'SortStructMembers'. We need a map key for bulkInsert
+// and a predicate for finding the insertion position of a single item.
+
+// Set this before using any of the below according to action
+static bool sortWatchDataAlphabetically = true;
 
-bool iNameLess(const QString &iname1, const QString &iname2)
+static bool watchDataLessThan(const QByteArray &iname1, int sortId1, const QByteArray &iname2, int sortId2)
 {
-    QString name1 = iname1.section('.', -1);
-    QString name2 = iname2.section('.', -1);
-    if (!name1.isEmpty() && !name2.isEmpty()) {
-        if (name1.at(0).isDigit() && name2.at(0).isDigit()) {
-            bool ok1 = false, ok2 = false;
-            int i1 = name1.toInt(&ok1), i2 = name2.toInt(&ok2);
-            if (ok1 && ok2)
-                return i1 < i2;
-        }
+    if (!sortWatchDataAlphabetically)
+        return sortId1 < sortId2;
+    // Get positions of last part of iname 'local.this.i1" -> "i1"
+    int cmpPos1 = iname1.lastIndexOf('.');
+    if (cmpPos1 == -1) {
+        cmpPos1 = 0;
+    } else {
+        cmpPos1++;
     }
-    return name1 < name2;
-}
+    int cmpPos2 = iname2.lastIndexOf('.');
+    if (cmpPos2 == -1) {
+        cmpPos2 = 0;
+    } else {
+        cmpPos2++;
+    }
+    // Are we looking at an array with numerical inames 'local.this.i1.0" ->
+    // Go by sort id.
+    if (cmpPos1 < iname1.size() && cmpPos2 < iname2.size()
+            && isdigit(iname1.at(cmpPos1)) && isdigit(iname2.at(cmpPos2)))
+        return sortId1 < sortId2;
+    // Alphabetically
+    return qstrcmp(iname1.constData() + cmpPos1, iname2.constData() + cmpPos2) < 0;
+}
+
+// Sort key for watch data consisting of iname and numerical sort id.
+struct WatchDataSortKey {
+    explicit WatchDataSortKey(const WatchData &wd) :
+             iname(wd.iname), sortId(wd.sortId) {}
+    QByteArray iname;
+    int sortId;
+};
 
-bool operator<(const IName &iname1, const IName &iname2)
+inline bool operator<(const WatchDataSortKey &k1, const WatchDataSortKey &k2)
 {
-    return iNameLess(iname1, iname2);
+    return watchDataLessThan(k1.iname, k1.sortId, k2.iname, k2.sortId);
 }
 
-static bool iNameSorter(const WatchItem *item1, const WatchItem *item2)
+bool watchItemSorter(const WatchItem *item1, const WatchItem *item2)
 {
-    return iNameLess(item1->iname, item2->iname);
+    return watchDataLessThan(item1->iname, item1->sortId, item2->iname, item2->sortId);
 }
 
 static int findInsertPosition(const QList<WatchItem *> &list, const WatchItem *item)
 {
-    QList<WatchItem *>::const_iterator it =
-        qLowerBound(list.begin(), list.end(), item, iNameSorter);
+    sortWatchDataAlphabetically = theDebuggerBoolSetting(SortStructMembers);
+    const QList<WatchItem *>::const_iterator it =
+        qLowerBound(list.begin(), list.end(), item, watchItemSorter);
     return it - list.begin();
 }
 
@@ -1045,7 +1067,7 @@ void WatchModel::insertData(const WatchData &data)
         item->parent = parent;
         item->generation = generationCounter;
         item->changed = true;
-        int n = findInsertPosition(parent->children, item);
+        const int n = findInsertPosition(parent->children, item);
         beginInsertRows(index, n, n);
         parent->children.insert(n, item);
         endInsertRows();
@@ -1078,10 +1100,11 @@ void WatchModel::insertBulkData(const QList<WatchData> &list)
     }
     QModelIndex index = watchIndex(parent);
 
-    QMap<IName, WatchData> newList;
-    typedef QMap<IName, WatchData>::iterator Iterator;
+    sortWatchDataAlphabetically = theDebuggerBoolSetting(SortStructMembers);
+    QMap<WatchDataSortKey, WatchData> newList;
+    typedef QMap<WatchDataSortKey, WatchData>::iterator Iterator;
     foreach (const WatchItem &data, list)
-        newList[data.iname] = data;
+        newList.insert(WatchDataSortKey(data), data);
     if (newList.size() != list.size()) {
         qDebug() << "LIST: ";
         foreach (const WatchItem &data, list)
@@ -1100,11 +1123,12 @@ void WatchModel::insertBulkData(const QList<WatchData> &list)
     QTC_ASSERT(newList.size() == list.size(), return);
 
     foreach (WatchItem *oldItem, parent->children) {
-        Iterator it = newList.find(oldItem->iname);
+        const WatchDataSortKey oldSortKey(*oldItem);
+        Iterator it = newList.find(oldSortKey);
         if (it == newList.end()) {
             WatchData data = *oldItem;
             data.generation = generationCounter;
-            newList[oldItem->iname] = data;
+            newList.insert(oldSortKey, data);
         } else {
             bool changed = !it->value.isEmpty()
                 && it->value != oldItem->value
diff --git a/src/plugins/debugger/watchutils.cpp b/src/plugins/debugger/watchutils.cpp
index 73016f69610..2c198204143 100644
--- a/src/plugins/debugger/watchutils.cpp
+++ b/src/plugins/debugger/watchutils.cpp
@@ -1666,7 +1666,7 @@ void setWatchDataDisplayedType(WatchData &data, const GdbMi &item)
 
 void parseWatchData(const QSet<QByteArray> &expandedINames,
     const WatchData &data0, const GdbMi &item,
-    bool sortMembers, QList<WatchData> *list)
+    QList<WatchData> *list)
 {
     //qDebug() << "HANDLE CHILDREN: " << data0.toString() << item.toString();
     WatchData data = data0;
@@ -1711,6 +1711,7 @@ void parseWatchData(const QSet<QByteArray> &expandedINames,
     int i = 0;
     foreach (const GdbMi &child, children.children()) {
         WatchData data1 = childtemplate;
+        data1.sortId = i++;
         GdbMi name = child.findChild("name");
         if (name.isValid())
             data1.name = _(name.data());
@@ -1722,11 +1723,6 @@ void parseWatchData(const QSet<QByteArray> &expandedINames,
         } else {
             data1.iname = data.iname;
             data1.iname += '.';
-            if (!sortMembers) {
-                char buf[10];
-                qsnprintf(buf, sizeof(buf) - 1, "%04d", i);
-                data1.iname += buf;
-            }
             data1.iname += data1.name.toLatin1();
         }
         if (!data1.name.isEmpty() && data1.name.at(0).isDigit())
@@ -1747,8 +1743,7 @@ void parseWatchData(const QSet<QByteArray> &expandedINames,
             //data1.name += " (" + skey + ")";
             data1.name = skey;
         }
-        parseWatchData(expandedINames, data1, child, sortMembers, list);
-        ++i;
+        parseWatchData(expandedINames, data1, child, list);
     }
 }
 
diff --git a/src/plugins/debugger/watchutils.h b/src/plugins/debugger/watchutils.h
index 61d3aa4c7fc..e1da61a37ab 100644
--- a/src/plugins/debugger/watchutils.h
+++ b/src/plugins/debugger/watchutils.h
@@ -257,7 +257,7 @@ void setWatchDataType(WatchData &data, const GdbMi &mi);
 void setWatchDataDisplayedType(WatchData &data, const GdbMi &mi);
 
 void parseWatchData(const QSet<QByteArray> &expandedINames,
-    const WatchData &parent, const GdbMi &child, bool sortMembers,
+    const WatchData &parent, const GdbMi &child,
     QList<WatchData> *insertions);
 
 } // namespace Internal
-- 
GitLab