From 6e93f4544d1f9b0fafc3f0d80a3f7049533fa8c0 Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Date: Thu, 2 Jul 2009 16:38:15 +0200
Subject: [PATCH] Display Q<>Pointers/Work towards displaying maps/QMaps in
 CDB.

Make dumpers pass on more size information initially, namely the
various Q<>Pointers. Introduce enum for those special template types
whose size does not vary with the arguments (Q<>Pointer,
std::allocators...) to make it more easily extensible.
Pass on some common QMapNode<> sizes as well.
Introduce an expression cache to the common QtDumperHelper
class and make dumpers pass some common expression values
(value offsets of common QMapNode<> incarnations).
Make CDBDumperHelper use the expression cache.
Extend dumper tester.
---
 share/qtcreator/gdbmacros/gdbmacros.cpp       |  57 ++++++-
 share/qtcreator/gdbmacros/test/main.cpp       | 127 ++++++++++----
 src/plugins/debugger/cdb/cdbdumperhelper.cpp  |  11 +-
 src/plugins/debugger/cdb/cdbdumperhelper.h    |   4 +-
 .../debugger/cdb/cdbstackframecontext.cpp     |  25 ++-
 src/plugins/debugger/watchutils.cpp           | 155 +++++++++++++-----
 src/plugins/debugger/watchutils.h             |  28 +++-
 7 files changed, 316 insertions(+), 91 deletions(-)

diff --git a/share/qtcreator/gdbmacros/gdbmacros.cpp b/share/qtcreator/gdbmacros/gdbmacros.cpp
index 03840ac854d..29b6feed765 100644
--- a/share/qtcreator/gdbmacros/gdbmacros.cpp
+++ b/share/qtcreator/gdbmacros/gdbmacros.cpp
@@ -50,6 +50,7 @@
 #if QT_VERSION >= 0x040500
 #include <QtCore/QSharedPointer>
 #include <QtCore/QSharedDataPointer>
+#include <QtCore/QSharedData>
 #include <QtCore/QWeakPointer>
 #endif
 
@@ -1345,6 +1346,11 @@ int hashOffset(bool optimizedIntKey, bool forKey, unsigned keySize, unsigned val
     }
 }
 
+#ifdef Q_CC_MSVC
+#  define MAP_NODE_TYPE_END ">"
+#else
+#  define MAP_NODE_TYPE_END " >"
+#endif
 
 static void qDumpQHash(QDumper &d)
 {
@@ -1407,7 +1413,7 @@ static void qDumpQHash(QDumper &d)
                     d.endItem();
                     d.beginItem("type");
                         d.put("'"NS"QHashNode<").put(keyType).put(",")
-                            .put(valueType).put(" >'");
+                            .put(valueType).put(MAP_NODE_TYPE_END"'");
                     d.endItem();
                 }
             d.endHash();
@@ -1763,7 +1769,7 @@ static void qDumpQMap(QDumper &d)
                     // actually, any type (even 'char') will do...
                     d.beginItem("type");
                         d.put(NS"QMapNode<").put(keyType).put(",");
-                        d.put(valueType).put(" >");
+                        d.put(valueType).put(MAP_NODE_TYPE_END);
                     d.endItem();
                     d.beginItem("exp");
                         d.put("*('"NS"QMapNode<").put(keyType).put(",");
@@ -1776,7 +1782,7 @@ static void qDumpQMap(QDumper &d)
 #else
                     d.beginItem("type");
                         d.put(NS"QMapData::Node<").put(keyType).put(",");
-                        d.put(valueType).put(" >");
+                        d.put(valueType).put(MAP_NODE_TYPE_END);
                     d.endItem();
                     d.beginItem("exp");
                         d.put("*('"NS"QMapData::Node<").put(keyType).put(",");
@@ -3036,6 +3042,27 @@ void *watchPoint(int x, int y)
 }
 #endif
 
+// Helper to write out common expression values for CDB:
+// Offsets of a map node value which looks like
+// "(size_t)&(('QMapNode<QString,QString >'*)0)->value")" in gdb syntax
+
+template <class Key, class Value>
+        inline QDumper & putQMapNodeOffsetExpression(const char *keyType,
+                                                     const char *valueType,
+                                                     QDumper &d)
+{
+    QMapNode<Key, Value> *mn = 0;
+    const int valueOffset = (char *)&(mn->value) - (char*)mn;
+    d.put("(size_t)&(('"NS"QMapNode<");
+    d.put(keyType);
+    d.put(',');
+    d.put(valueType);
+    d.put(">'*)0)->value=\"");
+    d.put(valueOffset);
+    d.put('"');
+    return d;
+}
+
 extern "C" Q_DECL_EXPORT
 void *qDumpObjectData440(
     int protocolVersion,
@@ -3136,8 +3163,30 @@ void *qDumpObjectData440(
 #endif
          .put("std::string=\"").put(sizeof(std::string)).put("\",")
          .put("std::wstring=\"").put(sizeof(std::wstring)).put("\",")
-         .put("std::allocator=\"").put(sizeof(std::allocator<int>))
+         .put("std::allocator=\"").put(sizeof(std::allocator<int>)).put("\",")
+#if QT_VERSION >= 0x040500
+         .put(NS"QSharedPointer=\"").put(sizeof(QSharedPointer<int>)).put("\",")
+         .put(NS"QSharedDataPointer=\"").put(sizeof(QSharedDataPointer<QSharedData>)).put("\",")
+         .put(NS"QWeakPointer=\"").put(sizeof(QWeakPointer<int>)).put("\",")
+#endif
+         .put("QPointer=\"").put(sizeof(QPointer<QObject>)).put("\",")
+         // Common map node types
+         .put(NS"QMapNode<int,int>=\"").put(sizeof(QMapNode<int,int >)).put("\",")
+         .put(NS"QMapNode<int,"NS"QString>=\"").put(sizeof(QMapNode<int, QString>)).put("\",")
+         .put(NS"QMapNode<int,"NS"QVariant>=\"").put(sizeof(QMapNode<int, QVariant>)).put("\",")
+         .put(NS"QMapNode<"NS"QString,int>=\"").put(sizeof(QMapNode<QString, int>)).put("\",")
+         .put(NS"QMapNode<"NS"QString,"NS"QString>=\"").put(sizeof(QMapNode<QString, QString>)).put("\",")
+         .put(NS"QMapNode<"NS"QString,"NS"QVariant>=\"").put(sizeof(QMapNode<QString, QVariant>))
          .put("\"}");
+        // Write out common expression values for CDB
+        d.put(",expressions={");
+        putQMapNodeOffsetExpression<int,int>("int", "int", d).put(',');
+        putQMapNodeOffsetExpression<int,QString>("int", NS"QString", d).put(',');
+        putQMapNodeOffsetExpression<int,QVariant>("int", NS"QVariant", d).put(',');
+        putQMapNodeOffsetExpression<QString,int>(NS"QString", "int", d).put(',');
+        putQMapNodeOffsetExpression<QString,QString>(NS"QString", NS"QString", d).put(',');
+        putQMapNodeOffsetExpression<QString,QVariant>(NS"QString", NS"QVariant", d);
+        d.put('}');
         d.disarm();
     }
 
diff --git a/share/qtcreator/gdbmacros/test/main.cpp b/share/qtcreator/gdbmacros/test/main.cpp
index 3d2adbd4a2a..fe6c2c6723e 100644
--- a/share/qtcreator/gdbmacros/test/main.cpp
+++ b/share/qtcreator/gdbmacros/test/main.cpp
@@ -29,12 +29,15 @@
 
 #include <QtCore/QStringList>
 #include <QtCore/QVector>
+#include <QtCore/QSharedPointer>
 #include <QtCore/QTimer>
+#include <QtCore/QMap>
 
 #include <string>
 #include <list>
 #include <vector>
 #include <set>
+#include <map>
 
 #include <stdio.h>
 #include <string.h>
@@ -102,6 +105,17 @@ static int dumpQString()
     return 0;
 }
 
+static int dumpQSharedPointerQString()
+{
+    QSharedPointer<QString> test(new QString(QLatin1String("hallo")));
+    prepareInBuffer("QSharedPointer", "local.sharedpointerqstring", "local.local.sharedpointerqstring", "QString");
+    qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(QString), 0, 0, 0);
+    fputs(qDumpOutBuffer, stdout);
+    fputc('\n', stdout);
+    QString uninitialized;
+    return 0;
+}
+
 static int dumpQStringList()
 {
     QStringList test = QStringList() << QLatin1String("item1") << QLatin1String("item2");
@@ -132,6 +146,34 @@ static int dumpQIntVector()
     return 0;
 }
 
+static int dumpQMapIntInt()
+{
+    QMap<int,int> test;
+    QMapNode<int,int> mapNode;
+    const int valueOffset = (char*)&(mapNode.value) - (char*)&mapNode;
+    test.insert(42, 43);
+    test.insert(43, 44);
+    prepareInBuffer("QMap", "local.qmapintint", "local.qmapintint", "int@int");
+    qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(int), sizeof(int), sizeof(mapNode), valueOffset);
+    fputs(qDumpOutBuffer, stdout);
+    fputc('\n', stdout);
+    return 0;
+}
+
+static int dumpQMapIntString()
+{
+    QMap<int,QString> test;
+    QMapNode<int,QString> mapNode;
+    const int valueOffset = (char*)&(mapNode.value) - (char*)&mapNode;
+    test.insert(42, QLatin1String("fortytwo"));
+    test.insert(43, QLatin1String("fortytree"));
+    prepareInBuffer("QMap", "local.qmapintqstring", "local.qmapintqstring", "int@QString");
+    qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(int), sizeof(QString), sizeof(mapNode), valueOffset);
+    fputs(qDumpOutBuffer, stdout);
+    fputc('\n', stdout);
+    return 0;
+}
+
 // ---------------  std types
 
 static int dumpStdString()
@@ -238,6 +280,20 @@ static int dumpStdStringSet()
     return 0;
 }
 
+static int dumpStdMapIntString()
+{
+    std::map<int,std::string> test;
+    std::map<int,std::string>::value_type entry(42, std::string("fortytwo"));
+    test.insert(entry);
+    const int valueOffset = (char*)&(entry.second) - (char*)&entry;
+    prepareInBuffer("std::map", "local.stdmapintstring", "local.stdmapintstring",
+                    "int@std::basic_string<char,std::char_traits<char>,std::allocator<char> >@std::less<int>@std::allocator<std::pair<const int,std::basic_string<char,std::char_traits<char>,std::allocator<char> > > >");
+    qDumpObjectData440(2, 42, testAddress(&test), 1, sizeof(int), sizeof(std::string), valueOffset, 0);
+    fputs(qDumpOutBuffer, stdout);
+    fputc('\n', stdout);
+    return 0;
+}
+
 static int dumpQObject()
 {
     // Requires the childOffset to be know, but that is not critical
@@ -249,6 +305,47 @@ static int dumpQObject()
     return 0;
 }
 
+static bool dumpType(const char *arg)
+{
+    if (!qstrcmp(arg, "QString"))
+        { dumpQString(); return true; }
+    if (!qstrcmp(arg, "QSharedPointer<QString>"))
+        { dumpQSharedPointerQString(); return true; }
+    if (!qstrcmp(arg, "QStringList"))
+        { dumpQStringList(); return true; }
+    if (!qstrcmp(arg, "QList<int>"))
+        { dumpQIntList(); return true; }
+    if (!qstrcmp(arg, "QList<std::string>"))
+        { dumpStdStringQList(); return true; }
+    if (!qstrcmp(arg, "QVector<int>"))
+        { dumpQIntVector(); return true; }
+    if (!qstrcmp(arg, "QMap<int,QString>"))
+        { dumpQMapIntString(); return true; }
+    if (!qstrcmp(arg, "QMap<int,int>"))
+        { dumpQMapIntInt(); return true; }
+    if (!qstrcmp(arg, "string"))
+        { dumpStdString(); return true; }
+    if (!qstrcmp(arg, "wstring"))
+        { dumpStdWString(); return true; }
+    if (!qstrcmp(arg, "list<int>"))
+        { dumpStdIntList(); return true; }
+    if (!qstrcmp(arg, "list<string>"))
+        { dumpStdStringList(); return true; }
+    if (!qstrcmp(arg, "vector<int>"))
+        { dumpStdIntVector(); return true; }
+    if (!qstrcmp(arg, "vector<string>"))
+        { dumpStdStringVector(); return true; }
+    if (!qstrcmp(arg, "set<int>"))
+        { dumpStdIntSet(); return true; }
+    if (!qstrcmp(arg, "set<string>"))
+        { dumpStdStringSet(); return true; }
+    if (!qstrcmp(arg, "map<int,string>"))
+        { dumpStdMapIntString(); return true; }
+    if (!qstrcmp(arg, "QObject"))
+        { dumpQObject(); return true; }
+    return false;
+}
+
 int main(int argc, char *argv[])
 {
     printf("Running query protocol\n");
@@ -266,34 +363,8 @@ int main(int argc, char *argv[])
             continue;
         }
         printf("\nTesting %s\n", arg);
-        if (!qstrcmp(arg, "QString"))
-            dumpQString();
-        if (!qstrcmp(arg, "QStringList"))
-            dumpQStringList();
-        if (!qstrcmp(arg, "QList<int>"))
-            dumpQIntList();
-        if (!qstrcmp(arg, "QList<std::string>"))
-            dumpStdStringQList();
-        if (!qstrcmp(arg, "QVector<int>"))
-            dumpQIntVector();
-        if (!qstrcmp(arg, "string"))
-            dumpStdString();
-        if (!qstrcmp(arg, "wstring"))
-            dumpStdWString();
-        if (!qstrcmp(arg, "list<int>"))
-            dumpStdIntList();
-        if (!qstrcmp(arg, "list<string>"))
-            dumpStdStringList();
-        if (!qstrcmp(arg, "vector<int>"))
-            dumpStdIntVector();
-        if (!qstrcmp(arg, "vector<string>"))
-            dumpStdStringVector();
-        if (!qstrcmp(arg, "set<int>"))
-            dumpStdIntSet();
-        if (!qstrcmp(arg, "set<string>"))
-            dumpStdStringSet();
-        if (!qstrcmp(arg, "QObject"))
-            dumpQObject();
+        if (!dumpType(arg))
+            printf("\nUnhandled type: %s\n", arg);
     }
     return 0;
 }
diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.cpp b/src/plugins/debugger/cdb/cdbdumperhelper.cpp
index 910eae6aa7e..0209a08e2b4 100644
--- a/src/plugins/debugger/cdb/cdbdumperhelper.cpp
+++ b/src/plugins/debugger/cdb/cdbdumperhelper.cpp
@@ -576,8 +576,9 @@ CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool
         return DumpOk;
     // Cache types that fail due to complicated template size expressions.
     // Exceptions OTOH might occur when accessing variables that are not
-    // yet initialized in a particular breakpoint. That should be ignored
-    if (der == DumpExecuteSizeFailed)
+    // yet initialized in a particular breakpoint. That should be ignored.
+    // Also fail for complex expression that were not cached/replaced by the helper.
+    if (der == DumpExecuteSizeFailed || der == DumpComplexExpressionEncountered)
         m_failedTypes.push_back(wd.type);    
     // log error
     *errorMessage = msgDumpFailed(wd, errorMessage);
@@ -611,6 +612,12 @@ CdbDumperHelper::DumpExecuteResult
             if (!sizeOk)
                 return DumpExecuteSizeFailed;
             ep = QString::number(size);
+            continue;
+        }
+        // We cannot evaluate any other expressions than 'sizeof()' ;-(
+        if (!ep.isEmpty() && !ep.at(0).isDigit()) {
+            *errorMessage = QString::fromLatin1("Unable to evaluate: '%1'").arg(ep);
+            return DumpComplexExpressionEncountered;
         }
     }
     // Execute call
diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.h b/src/plugins/debugger/cdb/cdbdumperhelper.h
index ccff93f0b62..3359093ab52 100644
--- a/src/plugins/debugger/cdb/cdbdumperhelper.h
+++ b/src/plugins/debugger/cdb/cdbdumperhelper.h
@@ -109,7 +109,9 @@ private:
     bool callDumper(const QString &call, const QByteArray &inBuffer, const char **outputPtr,
                     bool ignoreAccessViolation, QString *errorMessage);
 
-    enum DumpExecuteResult { DumpExecuteOk, DumpExecuteSizeFailed, DumpExecuteCallFailed };
+    enum DumpExecuteResult { DumpExecuteOk, DumpExecuteSizeFailed,
+                             DumpComplexExpressionEncountered,
+                             DumpExecuteCallFailed };
     DumpExecuteResult executeDump(const WatchData &wd,
                                   const QtDumperHelper::TypeData& td, bool dumpChildren, int source,
                                   QList<WatchData> *result, QString *errorMessage);
diff --git a/src/plugins/debugger/cdb/cdbstackframecontext.cpp b/src/plugins/debugger/cdb/cdbstackframecontext.cpp
index 0a4cbc1280b..9cd7589636e 100644
--- a/src/plugins/debugger/cdb/cdbstackframecontext.cpp
+++ b/src/plugins/debugger/cdb/cdbstackframecontext.cpp
@@ -112,7 +112,7 @@ WatchHandleDumperInserter::WatchHandleDumperInserter(WatchHandler *wh,
                                                        const SharedPointerCdbDumperHelper &dumper) :
     m_hexNullPattern(QLatin1String("0x0+")),
     m_wh(wh),
-    m_dumper(dumper)    
+    m_dumper(dumper)
 {
     Q_ASSERT(m_hexNullPattern.isValid());
 }
@@ -179,6 +179,10 @@ WatchHandleDumperInserter &WatchHandleDumperInserter::operator=(WatchData &wd)
     case CdbDumperHelper::DumpOk:
         if (debugCDBWatchHandling)
             qDebug() << "dumper triggered";
+        // Dumpers omit types for complicated templates
+        if (!m_dumperResult.isEmpty() && m_dumperResult.front().type.isEmpty()
+            && m_dumperResult.front().iname == wd.iname)
+            m_dumperResult.front().setType(wd.type);
         // Discard the original item and insert the dumper results
         foreach(const WatchData &dwd, m_dumperResult)
             m_wh->insertData(dwd);
@@ -245,13 +249,20 @@ bool CdbStackFrameContext::completeData(const WatchData &incompleteLocal,
                                                    errorMessage);
     }
 
-    // Expand dumper items (not implemented)
+    // Expand artifical dumper items
     if (incompleteLocal.source == OwnerDumper) {
-        if (debugCDBWatchHandling)
-            qDebug() << "ignored dumper item";
-        WatchData wd = incompleteLocal;
-        wd.setAllUnneeded();
-        wh->insertData(wd);
+        QList<WatchData> dumperResult;
+        const CdbDumperHelper::DumpResult dr = m_dumper->dumpType(incompleteLocal, true, OwnerDumper, &dumperResult, errorMessage);
+        if (dr == CdbDumperHelper::DumpOk) {
+            foreach(const WatchData &dwd, dumperResult)
+                wh->insertData(dwd);
+        } else {
+            const QString msg = QString::fromLatin1("Unable to further expand dumper watch data: %1 (%2): %3/%4").arg(incompleteLocal.name, incompleteLocal.type).arg(int(dr)).arg(*errorMessage);
+            qWarning("%s", qPrintable(msg));
+            WatchData wd = incompleteLocal;
+            wd.setAllUnneeded();
+            wh->insertData(wd);
+        }
         return true;
     }
 
diff --git a/src/plugins/debugger/watchutils.cpp b/src/plugins/debugger/watchutils.cpp
index 60b75fdc761..6933b68ff8e 100644
--- a/src/plugins/debugger/watchutils.cpp
+++ b/src/plugins/debugger/watchutils.cpp
@@ -560,14 +560,10 @@ void QtDumperHelper::TypeData::clear()
 }
 
 // ----------------- QtDumperHelper
-const QString stdAllocatorPrefix = QLatin1String("std::allocator");
-
 QtDumperHelper::QtDumperHelper() :
-    m_intSize(0),
-    m_pointerSize(0),
-    m_stdAllocatorSize(0),
     m_qtVersion(0)
 {
+    qFill(m_specialSizes, m_specialSizes + SpecialSizeCount, 0);
 }
 
 void QtDumperHelper::clear()
@@ -576,9 +572,8 @@ void QtDumperHelper::clear()
     m_qtVersion = 0;
     m_qtNamespace.clear();
     m_sizeCache.clear();
-    m_intSize = 0;
-    m_pointerSize = 0;
-    m_stdAllocatorSize = 0;
+    qFill(m_specialSizes, m_specialSizes + SpecialSizeCount, 0);
+    m_expressionCache.clear();
 }
 
 static inline void formatQtVersion(int v, QTextStream &str)
@@ -593,13 +588,15 @@ QString QtDumperHelper::toString(bool debug) const
         QTextStream str(&rc);
         str << "version=";
         formatQtVersion(m_qtVersion, str);
-        str << " namespace='" << m_qtNamespace << "'," << m_nameTypeMap.size() << " known types: ";
+        str << " namespace='" << m_qtNamespace << "'," << m_nameTypeMap.size() << " known types <type enum>: ";
         const NameTypeMap::const_iterator cend = m_nameTypeMap.constEnd();
         for (NameTypeMap::const_iterator it = m_nameTypeMap.constBegin(); it != cend; ++it) {
             str <<",[" << it.key() << ',' << it.value() << ']';
         }
-        str << "Sizes: intsize=" << m_intSize << " pointer size=" << m_pointerSize
-                << " allocatorsize=" << m_stdAllocatorSize;
+        str << "\nSpecial size: ";
+        for (int i = 0; i < SpecialSizeCount; i++)
+            str << ' ' << m_specialSizes[i];
+        str << "\nSize cache: ";
         const SizeCache::const_iterator scend = m_sizeCache.constEnd();
         for (SizeCache::const_iterator it = m_sizeCache.constBegin(); it != scend; ++it) {
             str << ' ' << it.key() << '=' << it.value();
@@ -693,22 +690,23 @@ QtDumperHelper::Type QtDumperHelper::specialType(QString s)
     return UnknownType;
 }
 
-bool QtDumperHelper::needsExpressionSyntax(Type t)
+QtDumperHelper::ExpressionRequirement QtDumperHelper::expressionRequirements(Type t)
 {
     switch (t) {
-        case QAbstractItemType:
-        case QObjectSlotType:
-        case QObjectSignalType:
-        case QMapType:
-        case QVectorType:
-        case QMultiMapType:
-        case QMapNodeType:
-        case StdMapType:
-            return true;
-        default:
-            break;
+    case QAbstractItemType:
+    case QObjectSlotType:
+    case QObjectSignalType:
+    case QVectorType:
+    case StdMapType:
+        return NeedsComplexExpression;
+    case QMapType:
+    case QMultiMapType:
+    case QMapNodeType:
+        return NeedsCachedExpression;
+    default:
+        break;
     }
-    return false;
+    return NeedsNoExpression;
 }
 
 QString QtDumperHelper::qtVersionString() const
@@ -745,7 +743,7 @@ void QtDumperHelper::parseQueryTypes(const QStringList &l, Debugger debugger)
         const Type t = specialType(l.at(i));
         if (t != UnknownType) {
             // Exclude types that require expression syntax for CDB
-            if (debugger == GdbDebugger || !needsExpressionSyntax(t))
+            if (debugger == GdbDebugger || expressionRequirements(t) != NeedsComplexExpression)
                 m_nameTypeMap.insert(l.at(i), t);
         } else {
             m_nameTypeMap.insert(l.at(i), SupportedType);
@@ -940,6 +938,7 @@ public:
         QString qtVersion;
         QStringList types;
         QList<SizeEntry> sizes;
+        QMap<QString, QString> expressionCache;
     };
 
     inline Data data() const { return m_data; }
@@ -952,10 +951,12 @@ protected:
     virtual bool handleValue(const char *k, int size);
 
 private:
-    enum Mode { None, ExpectingDumpers, ExpectingVersion, ExpectingNameSpace, ExpectingSizes };
+    enum Mode { None, ExpectingDumpers, ExpectingVersion,
+                ExpectingNameSpace, ExpectingSizes, ExpectingExpressionCache };
     Mode m_mode;
     Data m_data;
     QString m_lastSizeType;
+    QString m_lastExpression;
 };
 
 QueryDumperParser::QueryDumperParser(const char *s) :
@@ -965,10 +966,16 @@ QueryDumperParser::QueryDumperParser(const char *s) :
 }
 
 bool QueryDumperParser::handleKeyword(const char *k, int size)        
-{    
-    if (m_mode == ExpectingSizes) {
+{
+    switch (m_mode) {
+    case ExpectingSizes:
         m_lastSizeType = QString::fromLatin1(k, size);
         return true;
+    case ExpectingExpressionCache:
+        m_lastExpression = QString::fromLatin1(k, size);
+        return true;
+    default:
+       break;
     }
     if (!qstrncmp(k, "dumpers", size)) {
         m_mode = ExpectingDumpers;
@@ -986,6 +993,10 @@ bool QueryDumperParser::handleKeyword(const char *k, int size)
         m_mode = ExpectingSizes;
         return true;
     }
+    if (!qstrncmp(k, "expressions", size)) {
+        m_mode = ExpectingExpressionCache;
+        return true;
+    }
     qWarning("%s Unexpected keyword %s.\n", Q_FUNC_INFO, QByteArray(k, size).constData());
     return false;
 }
@@ -1026,6 +1037,9 @@ bool QueryDumperParser::handleValue(const char *k, int size)
     case ExpectingSizes:
         m_data.sizes.push_back(SizeEntry(m_lastSizeType, QString::fromLatin1(k, size).toInt()));
         break;
+    case ExpectingExpressionCache:
+        m_data.expressionCache.insert(m_lastExpression, QString::fromLatin1(k, size));
+        break;
     }
     return true;
 }
@@ -1042,25 +1056,24 @@ bool QtDumperHelper::parseQuery(const char *data, Debugger debugger)
     parseQueryTypes(parser.data().types, debugger);
     foreach (const QueryDumperParser::SizeEntry &se, parser.data().sizes)
         addSize(se.first, se.second);
+    m_expressionCache = parser.data().expressionCache;
+    qDebug() << m_expressionCache;
     return true;
 }
 
 void QtDumperHelper::addSize(const QString &name, int size)
 {
     // Special interest cases
+    if (name == QLatin1String("char*")) {
+        m_specialSizes[PointerSize] = size;
+        return;
+    }
+    const SpecialSizeType st = specialSizeType(name);
+    if (st != SpecialSizeCount) {
+        m_specialSizes[st] = size;
+        return;
+    }
     do {
-        if (name == QLatin1String("char*")) {
-            m_pointerSize = size;
-            break;
-        }
-        if (name == QLatin1String("int")) {
-            m_intSize = size;
-            break;
-        }
-        if (name.startsWith(stdAllocatorPrefix)) {
-            m_stdAllocatorSize = size;
-            break;
-        }
         if (name == QLatin1String("std::string")) {
             m_sizeCache.insert(QLatin1String("std::basic_string<char,std::char_traits<char>,std::allocator<char>>"), size);
             break;
@@ -1104,11 +1117,13 @@ QtDumperHelper::TypeData QtDumperHelper::typeData(const QString &typeName) const
 QString QtDumperHelper::evaluationSizeofTypeExpression(const QString &typeName,
                                                        Debugger /* debugger */) const
 {
-    // Look up fixed types
-    if (m_pointerSize && isPointerType(typeName))
-        return QString::number(m_pointerSize);
-    if (m_stdAllocatorSize && typeName.startsWith(stdAllocatorPrefix))
-        return QString::number(m_stdAllocatorSize);
+    // Look up special size types
+    const SpecialSizeType st = specialSizeType(typeName);
+    if (st != SpecialSizeCount) {
+        if (const int size = m_specialSizes[st])
+            return QString::number(size);
+    }
+    // Look up size cache
     const SizeCache::const_iterator sit = m_sizeCache.constFind(typeName);
     if (sit != m_sizeCache.constEnd())
         return QString::number(sit.value());
@@ -1116,6 +1131,42 @@ QString QtDumperHelper::evaluationSizeofTypeExpression(const QString &typeName,
     return sizeofTypeExpression(typeName);
 }
 
+QtDumperHelper::SpecialSizeType QtDumperHelper::specialSizeType(const QString &typeName)
+{
+    if (isPointerType(typeName))
+        return PointerSize;
+    static const QString intType = QLatin1String("int");
+    static const QString stdAllocatorPrefix = QLatin1String("std::allocator");
+    static const QString qPointerPrefix = QLatin1String("QPointer");
+    static const QString qSharedPointerPrefix = QLatin1String("QSharedPointer");
+    static const QString qSharedDataPointerPrefix = QLatin1String("QSharedDataPointer");
+    static const QString qWeakPointerPrefix = QLatin1String("QWeakPointer");
+    if (typeName == intType)
+        return IntSize;
+    if (typeName.startsWith(stdAllocatorPrefix))
+        return StdAllocatorSize;
+    if (typeName.startsWith(qPointerPrefix))
+        return QPointerSize;
+    if (typeName.startsWith(qSharedPointerPrefix))
+        return QSharedPointerSize;
+    if (typeName.startsWith(qSharedDataPointerPrefix))
+        return QSharedDataPointerSize;
+    if (typeName.startsWith(qWeakPointerPrefix))
+        return QWeakPointerSize;
+    return SpecialSizeCount;
+}
+
+static inline bool isInteger(const QString &n)
+{
+    const int size = n.size();
+    if (!size)
+        return false;
+    for (int i = 0; i < size; i++)
+        if (!n.at(i).isDigit())
+            return false;
+    return true;
+}
+
 void QtDumperHelper::evaluationParameters(const WatchData &data,
                                           const TypeData &td,
                                           Debugger debugger,
@@ -1265,6 +1316,20 @@ void QtDumperHelper::evaluationParameters(const WatchData &data,
         break;
     }
 
+    // Look up expressions in the cache
+    if (!m_expressionCache.empty()) {
+        const QMap<QString, QString>::const_iterator excCend = m_expressionCache.constEnd();
+        const QStringList::iterator eend = extraArgs.end();
+        for (QStringList::iterator it = extraArgs.begin(); it != eend; ++it) {
+            QString &e = *it;
+            if (!e.isEmpty() && e != zero && !isInteger(e)) {
+                const QMap<QString, QString>::const_iterator eit = m_expressionCache.constFind(e);
+                if (eit != excCend)
+                    e = eit.value();
+            }
+        }
+    }
+
     inBuffer->clear();
     inBuffer->append(outertype.toUtf8());
     inBuffer->append('\0');
diff --git a/src/plugins/debugger/watchutils.h b/src/plugins/debugger/watchutils.h
index 52339fb0bfd..d9a43d136f9 100644
--- a/src/plugins/debugger/watchutils.h
+++ b/src/plugins/debugger/watchutils.h
@@ -198,7 +198,16 @@ public:
     // 'data' excludes the leading indicator character.
     static bool parseValue(const char *data, QtDumperResult *r);
 
-    static bool needsExpressionSyntax(Type t);
+    // What kind of debugger expressions are required to dump that type.
+    // A debugger with restricted expression syntax can handle
+    // 'NeedsNoExpression' and 'NeedsCachedExpression' if it is found in
+    // the cache.
+    enum ExpressionRequirement {
+        NeedsNoExpression,     // None, easy.
+        NeedsCachedExpression, // Common values might be found in expression cache.
+        NeedsComplexExpression // Totally arbitrary, adress-dependent expressions
+    };
+    static ExpressionRequirement expressionRequirements(Type t);
 
     QString toString(bool debug = false) const;
 
@@ -214,9 +223,20 @@ private:
 
     NameTypeMap m_nameTypeMap;
     SizeCache m_sizeCache;
-    int m_intSize;
-    int m_pointerSize;
-    int m_stdAllocatorSize;
+
+    // The initial dumper query function returns sizes of some special
+    // types to aid CDB since it cannot determine the size of classes.
+    // They are not complete (std::allocator<X>).
+    enum SpecialSizeType { IntSize, PointerSize, StdAllocatorSize,
+                           QSharedPointerSize, QSharedDataPointerSize,
+                           QWeakPointerSize, QPointerSize, SpecialSizeCount };
+
+    // Resolve name to enumeration or SpecialSizeCount (invalid)
+    static SpecialSizeType specialSizeType(const QString &t);
+
+    int m_specialSizes[SpecialSizeCount];
+
+    QMap<QString, QString> m_expressionCache;
     int m_qtVersion;
     QString m_qtNamespace;
 };
-- 
GitLab