watchutils.cpp 53.7 KB
Newer Older
1
2
3
4
5
6
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, 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.
**
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
27
28
29
30
**
**************************************************************************/

#include "watchutils.h"
31
#include "watchhandler.h"
32
#include "gdb/gdbmi.h"
33
#include <utils/qtcassert.h>
34

35
36
#include <coreplugin/ifile.h>

37
38
39
40
41
42
43
44
45
#include <texteditor/basetexteditor.h>
#include <texteditor/basetextmark.h>
#include <texteditor/itexteditor.h>
#include <texteditor/texteditorconstants.h>

#include <cpptools/cppmodelmanagerinterface.h>
#include <cpptools/cpptoolsconstants.h>

#include <cplusplus/ExpressionUnderCursor.h>
46
47
48
#include <cplusplus/Overview.h>
#include <Symbols.h>
#include <Scope.h>
49
50
51

#include <extensionsystem/pluginmanager.h>

52
53
54
#include <QtCore/QDebug>
#include <QtCore/QTime>
#include <QtCore/QStringList>
55
56
#include <QtCore/QCoreApplication>
#include <QtCore/QTextStream>
57
#include <QtCore/QHash>
58

59
60
61
#include <QtGui/QTextCursor>
#include <QtGui/QPlainTextEdit>

62
63
64
65
#include <string.h>
#include <ctype.h>

enum { debug = 0 };
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

// Debug helpers for code model. @todo: Move to some CppTools library?
namespace CPlusPlus {

static void debugCppSymbolRecursion(QTextStream &str, const Overview &o,
                                    const Symbol &s, bool doRecurse = true,
                                    int recursion = 0)
{
    for (int i = 0; i < recursion; i++)
        str << "  ";
    str << "Symbol: " << o.prettyName(s.name()) << " at line " << s.line();
    if (s.isFunction())
        str << " function";
    if (s.isClass())
        str << " class";
    if (s.isDeclaration())
        str << " declaration";
    if (s.isBlock())
        str << " block";
    if (doRecurse && s.isScopedSymbol()) {
        const ScopedSymbol *scoped = s.asScopedSymbol();
        const int size =  scoped->memberCount();
        str << " scoped symbol of " << size << '\n';
        for (int m = 0; m < size; m++)
            debugCppSymbolRecursion(str, o, *scoped->memberAt(m), true, recursion + 1);
    } else {
        str << '\n';
    }
}

QDebug operator<<(QDebug d, const Symbol &s)
{
    QString output;
    CPlusPlus::Overview o;
    QTextStream str(&output);
    debugCppSymbolRecursion(str, o, s, true, 0);
    d.nospace() << output;
    return d;
}

QDebug operator<<(QDebug d, const Scope &scope)
{
    QString output;
    Overview o;
    QTextStream str(&output);
    const int size =  scope.symbolCount();
    str << "Scope of " << size;
    if (scope.isNamespaceScope())
        str << " namespace";
    if (scope.isClassScope())
        str << " class";
    if (scope.isEnumScope())
        str << " enum";
    if (scope.isBlockScope())
        str << " block";
    if (scope.isFunctionScope())
        str << " function";
    if (scope.isPrototypeScope())
        str << " prototype";
    if (const Symbol *owner = scope.owner()) {
        str << " owner: ";
        debugCppSymbolRecursion(str, o, *owner, false, 0);
    } else {
        str << " 0-owner\n";
    }
    for (int s = 0; s < size; s++)
        debugCppSymbolRecursion(str, o, *scope.symbolAt(s), true, 2);
    d.nospace() << output;
    return d;
}
} // namespace CPlusPlus

138
139
140
namespace Debugger {
namespace Internal {

141
QByteArray dotEscape(QByteArray str)
142
{
143
144
145
    str.replace(' ', '.');
    str.replace('\\', '.');
    str.replace('/', '.');
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
    return str;
}

QString currentTime()
{
    return QTime::currentTime().toString(QLatin1String("hh:mm:ss.zzz"));
}

bool isSkippableFunction(const QString &funcName, const QString &fileName)
{
    if (fileName.endsWith(QLatin1String("kernel/qobject.cpp")))
        return true;
    if (fileName.endsWith(QLatin1String("kernel/moc_qobject.cpp")))
        return true;
    if (fileName.endsWith(QLatin1String("kernel/qmetaobject.cpp")))
        return true;
162
163
    if (fileName.endsWith(QLatin1String("kernel/qmetaobject_p.h")))
        return true;
164
165
166
    if (fileName.endsWith(QLatin1String(".moc")))
        return true;

167
    if (funcName.endsWith(QLatin1String("::qt_metacall")))
168
        return true;
169
    if (funcName.endsWith(QLatin1String("::d_func")))
170
        return true;
171
    if (funcName.endsWith(QLatin1String("::q_func")))
172
        return true;
173
174
175
176
177
178
179
180

    return false;
}

bool isLeavableFunction(const QString &funcName, const QString &fileName)
{
    if (funcName.endsWith(QLatin1String("QObjectPrivate::setCurrentSender")))
        return true;
181
182
    if (funcName.endsWith(QLatin1String("QMutexPool::get")))
        return true;
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
    if (fileName.endsWith(QLatin1String("kernel/qmetaobject.cpp"))
            && funcName.endsWith(QLatin1String("QMetaObject::methodOffset")))
        return true;
    if (fileName.endsWith(QLatin1String("kernel/qobject.h")))
        return true;
    if (fileName.endsWith(QLatin1String("kernel/qobject.cpp"))
            && funcName.endsWith(QLatin1String("QObjectConnectionListVector::at")))
        return true;
    if (fileName.endsWith(QLatin1String("kernel/qobject.cpp"))
            && funcName.endsWith(QLatin1String("~QObject")))
        return true;
    if (fileName.endsWith(QLatin1String("thread/qmutex.cpp")))
        return true;
    if (fileName.endsWith(QLatin1String("thread/qthread.cpp")))
        return true;
    if (fileName.endsWith(QLatin1String("thread/qthread_unix.cpp")))
        return true;
    if (fileName.endsWith(QLatin1String("thread/qmutex.h")))
        return true;
    if (fileName.contains(QLatin1String("thread/qbasicatomic")))
        return true;
    if (fileName.contains(QLatin1String("thread/qorderedmutexlocker_p")))
        return true;
    if (fileName.contains(QLatin1String("arch/qatomic")))
        return true;
    if (fileName.endsWith(QLatin1String("tools/qvector.h")))
        return true;
    if (fileName.endsWith(QLatin1String("tools/qlist.h")))
        return true;
    if (fileName.endsWith(QLatin1String("tools/qhash.h")))
        return true;
    if (fileName.endsWith(QLatin1String("tools/qmap.h")))
        return true;
216
217
    if (fileName.endsWith(QLatin1String("tools/qshareddata.h")))
        return true;
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
    if (fileName.endsWith(QLatin1String("tools/qstring.h")))
        return true;
    if (fileName.endsWith(QLatin1String("global/qglobal.h")))
        return true;

    return false;
}

bool hasLetterOrNumber(const QString &exp)
{
    const QChar underscore = QLatin1Char('_');
    for (int i = exp.size(); --i >= 0; )
        if (exp.at(i).isLetterOrNumber() || exp.at(i) == underscore)
            return true;
    return false;
}

bool hasSideEffects(const QString &exp)
{
    // FIXME: complete?
    return exp.contains(QLatin1String("-="))
        || exp.contains(QLatin1String("+="))
        || exp.contains(QLatin1String("/="))
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
241
        || exp.contains(QLatin1String("%="))
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
        || exp.contains(QLatin1String("*="))
        || exp.contains(QLatin1String("&="))
        || exp.contains(QLatin1String("|="))
        || exp.contains(QLatin1String("^="))
        || exp.contains(QLatin1String("--"))
        || exp.contains(QLatin1String("++"));
}

bool isKeyWord(const QString &exp)
{
    // FIXME: incomplete
    return exp == QLatin1String("class")
        || exp == QLatin1String("const")
        || exp == QLatin1String("do")
        || exp == QLatin1String("if")
        || exp == QLatin1String("return")
        || exp == QLatin1String("struct")
        || exp == QLatin1String("template")
        || exp == QLatin1String("void")
        || exp == QLatin1String("volatile")
        || exp == QLatin1String("while");
}

bool isPointerType(const QString &type)
{
    return type.endsWith(QLatin1Char('*')) || type.endsWith(QLatin1String("* const"));
}

hjk's avatar
hjk committed
270
271
272
273
274
275
276
bool isCharPointerType(const QString &type)
{
    return type == QLatin1String("char *")
        || type == QLatin1String("const char *")
        || type == QLatin1String("char const *");
}

277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
bool startsWithDigit(const QString &str)
{
    return !str.isEmpty() && str.at(0).isDigit();
}

QString stripPointerType(QString type)
{
    if (type.endsWith(QLatin1Char('*')))
        type.chop(1);
    if (type.endsWith(QLatin1String("* const")))
        type.chop(7);
    if (type.endsWith(QLatin1Char(' ')))
        type.chop(1);
    return type;
}

293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
/* getUninitializedVariables(): Get variables that are not initialized
 * at a certain line of a function from the code model to be able to
 * indicate them as not in scope in the locals view.
 * Find document + function in the code model, do a double check and
 * collect declarative symbols that are in the function past or on
 * the current line. blockRecursion() recurses up the scopes
 * and collect symbols declared past or on the current line.
 * Recursion goes up from the innermost scope, keeping a map
 * of occurrences seen, to be able to derive the names of
 * shadowed variables as the debugger sees them:
\code
int x;             // Occurrence (1), should be reported as "x <shadowed 1>"
if (true) {
   int x = 5; (2)  // Occurrence (2), should be reported as "x"
}
\endcode
 */

typedef QHash<QString, int> SeenHash;

static void blockRecursion(const CPlusPlus::Overview &overview,
                           const CPlusPlus::Scope *scope,
                           unsigned line,
                           QStringList *uninitializedVariables,
                           SeenHash *seenHash,
                           int level = 0)
{
    const int size = scope->symbolCount();
    for (int s = 0; s < size; s++){
        const CPlusPlus::Symbol *symbol = scope->symbolAt(s);
        if (symbol->isDeclaration()) {
            // Find out about shadowed symbols by bookkeeping
            // the already seen occurrences in a hash.
            const QString name = overview.prettyName(symbol->name());
            SeenHash::iterator it = seenHash->find(name);
            if (it == seenHash->end()) {
                it = seenHash->insert(name, 0);
            } else {
                ++(it.value());
332
            }
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
            // Is the declaration on or past the current line, that is,
            // the variable not initialized.
            if (symbol->line() >= line)
                uninitializedVariables->push_back(WatchData::shadowedName(name, it.value()));
        }
    }
    // Next block scope.
    if (const CPlusPlus::Scope *enclosingScope = scope->enclosingBlockScope())
        blockRecursion(overview, enclosingScope, line, uninitializedVariables, seenHash, level + 1);
}

// Inline helper with integer error return codes.
static inline
int getUninitializedVariablesI(const CPlusPlus::Snapshot &snapshot,
                               const QString &functionName,
                               const QString &file,
                               int line,
                               QStringList *uninitializedVariables)
{
    uninitializedVariables->clear();
    // Find document
354
    if (snapshot.isEmpty() || functionName.isEmpty() || file.isEmpty() || line < 1)
355
        return 1;
356
357
    const CPlusPlus::Snapshot::const_iterator docIt = snapshot.find(file);
    if (docIt == snapshot.end())
358
359
360
361
362
363
364
        return 2;
    const CPlusPlus::Document::Ptr doc = docIt.value();
    // Look at symbol at line and find its function. Either it is the
    // function itself or some expression/variable.
    const CPlusPlus::Symbol *symbolAtLine = doc->findSymbolAt(line, 0);
    if (!symbolAtLine)
        return 4;
365
366
    // First figure out the function to do a safety name check
    // and the innermost scope at cursor position
367
    const CPlusPlus::Function *function = 0;
368
369
    const CPlusPlus::Scope *innerMostScope = 0;
    if (symbolAtLine->isFunction()) {
370
        function = symbolAtLine->asFunction();
371
372
373
        if (function->memberCount() == 1) // Skip over function block
            if (CPlusPlus::Block *block = function->memberAt(0)->asBlock())
                innerMostScope = block->members();
374
    } else {
375
        if (const CPlusPlus::Scope *functionScope = symbolAtLine->enclosingFunctionScope()) {
376
            function = functionScope->owner()->asFunction();
377
378
379
380
            innerMostScope = symbolAtLine->isBlock() ?
                             symbolAtLine->asBlock()->members() :
                             symbolAtLine->enclosingBlockScope();
        }
381
    }
382
    if (!function || !innerMostScope)
383
384
385
386
387
388
389
390
391
392
393
394
395
        return 7;
    // Compare function names with a bit off fuzz,
    // skipping modules from a CDB symbol "lib!foo" or namespaces
    // that the code model does not show at this point
    CPlusPlus::Overview overview;
    const QString name = overview.prettyName(function->name());
    if (!functionName.endsWith(name))
        return 11;
    if (functionName.size() > name.size()) {
        const char previousChar = functionName.at(functionName.size() - name.size() - 1).toLatin1();
        if (previousChar != ':' && previousChar != '!' )
            return 11;
    }
396
    // Starting from the innermost block scope, collect declarations.
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
    SeenHash seenHash;
    blockRecursion(overview, innerMostScope, line, uninitializedVariables, &seenHash);
    return 0;
}

bool getUninitializedVariables(const CPlusPlus::Snapshot &snapshot,
                               const QString &function,
                               const QString &file,
                               int line,
                               QStringList *uninitializedVariables)
{
    const int rc = getUninitializedVariablesI(snapshot, function, file, line, uninitializedVariables);
    if (debug) {
        QString msg;
        QTextStream str(&msg);
        str << "getUninitializedVariables() " << function << ' ' << file << ':' << line
                << " returns (int) " << rc << " '"
                << uninitializedVariables->join(QString(QLatin1Char(','))) << '\'';
        if (rc)
416
            str << " of " << snapshot.size() << " documents";
417
418
419
420
421
        qDebug() << msg;
    }
    return rc == 0;
}

422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
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
QString gdbQuoteTypes(const QString &type)
{
    // gdb does not understand sizeof(Core::IFile*).
    // "sizeof('Core::IFile*')" is also not acceptable,
    // it needs to be "sizeof('Core::IFile'*)"
    //
    // We never will have a perfect solution here (even if we had a full blown
    // C++ parser as we do not have information on what is a type and what is
    // a variable name. So "a<b>::c" could either be two comparisons of values
    // 'a', 'b' and '::c', or a nested type 'c' in a template 'a<b>'. We
    // assume here it is the latter.
    //return type;

    // (*('myns::QPointer<myns::QObject>*'*)0x684060)" is not acceptable
    // (*('myns::QPointer<myns::QObject>'**)0x684060)" is acceptable
    if (isPointerType(type))
        return gdbQuoteTypes(stripPointerType(type)) + QLatin1Char('*');

    QString accu;
    QString result;
    int templateLevel = 0;

    const QChar colon = QLatin1Char(':');
    const QChar singleQuote = QLatin1Char('\'');
    const QChar lessThan = QLatin1Char('<');
    const QChar greaterThan = QLatin1Char('>');
    for (int i = 0; i != type.size(); ++i) {
        const QChar c = type.at(i);
        if (c.isLetterOrNumber() || c == QLatin1Char('_') || c == colon || c == QLatin1Char(' ')) {
            accu += c;
        } else if (c == lessThan) {
            ++templateLevel;
            accu += c;
        } else if (c == greaterThan) {
            --templateLevel;
            accu += c;
        } else if (templateLevel > 0) {
            accu += c;
        } else {
            if (accu.contains(colon) || accu.contains(lessThan))
                result += singleQuote + accu + singleQuote;
            else
                result += accu;
            accu.clear();
            result += c;
        }
    }
    if (accu.contains(colon) || accu.contains(lessThan))
        result += singleQuote + accu + singleQuote;
    else
        result += accu;
    //qDebug() << "GDB_QUOTING" << type << " TO " << result;

    return result;
}

bool extractTemplate(const QString &type, QString *tmplate, QString *inner)
{
    // Input "Template<Inner1,Inner2,...>::Foo" will return "Template::Foo" in
    // 'tmplate' and "Inner1@Inner2@..." etc in 'inner'. Result indicates
    // whether parsing was successful
483
484
485
486
487
    // Gdb inserts a blank after each comma which we would like to avoid
    tmplate->clear();
    inner->clear();
    if (!type.contains(QLatin1Char('<')))
        return  false;
488
489
    int level = 0;
    bool skipSpace = false;
490
    const int size = type.size();
491

492
    for (int i = 0; i != size; ++i) {
493
        const QChar c = type.at(i);
494
495
496
        const char asciiChar = c.toAscii();
        switch (asciiChar) {
        case '<':
497
498
            *(level == 0 ? tmplate : inner) += c;
            ++level;
499
500
            break;
        case '>':
501
502
            --level;
            *(level == 0 ? tmplate : inner) += c;
503
504
            break;
        case ',':
505
506
            *inner += (level == 1) ? QLatin1Char('@') : QLatin1Char(',');
            skipSpace = true;
507
508
509
510
511
512
513
            break;
        default:
            if (!skipSpace || asciiChar != ' ') {
                *(level == 0 ? tmplate : inner) += c;
                skipSpace = false;
            }
            break;
514
515
516
517
518
        }
    }
    *tmplate = tmplate->trimmed();
    *tmplate = tmplate->remove(QLatin1String("<>"));
    *inner = inner->trimmed();
519
    // qDebug() << "EXTRACT TEMPLATE: " << *tmplate << *inner << " FROM " << type;
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
    return !inner->isEmpty();
}

QString extractTypeFromPTypeOutput(const QString &str)
{
    int pos0 = str.indexOf(QLatin1Char('='));
    int pos1 = str.indexOf(QLatin1Char('{'));
    int pos2 = str.lastIndexOf(QLatin1Char('}'));
    QString res = str;
    if (pos0 != -1 && pos1 != -1 && pos2 != -1)
        res = str.mid(pos0 + 2, pos1 - 1 - pos0)
            + QLatin1String(" ... ") + str.right(str.size() - pos2);
    return res.simplified();
}

535
bool isIntType(const QString &type)
536
537
538
{
    static const QStringList types = QStringList()
        << QLatin1String("char") << QLatin1String("int") << QLatin1String("short")
539
540
        << QLatin1String("long") << QLatin1String("bool")
        << QLatin1String("signed char") << QLatin1String("unsigned")
541
        << QLatin1String("unsigned char") << QLatin1String("unsigned long")
542
543
544
545
546
547
548
        << QLatin1String("long long")  << QLatin1String("unsigned long long")
        << QLatin1String("qint16") << QLatin1String("quint16")
        << QLatin1String("qint32") << QLatin1String("quint32")
        << QLatin1String("qint64") << QLatin1String("quint64");
    return type.endsWith(QLatin1String(" int"))
            || type.endsWith(QLatin1String(" int64"))
            || types.contains(type);
549
550
}

551
552
553
554
555
556
557
bool isSymbianIntType(const QString &type)
{
    static const QStringList types = QStringList()
        << QLatin1String("TInt") << QLatin1String("TBool");
    return types.contains(type);
}

558
559
560
561
562
563
564
bool isIntOrFloatType(const QString &type)
{
    static const QStringList types = QStringList()
        << QLatin1String("float") << QLatin1String("double");
    return isIntType(type) || types.contains(type);
}

565
566
567
568
GuessChildrenResult guessChildren(const QString &type)
{
    if (isIntOrFloatType(type))
        return HasNoChildren;
hjk's avatar
hjk committed
569
570
    if (isCharPointerType(type))
        return HasNoChildren;
571
572
573
574
575
576
577
    if (isPointerType(type))
        return HasChildren;
    if (type.endsWith(QLatin1String("QString")))
        return HasNoChildren;
    return HasPossiblyChildren;
}

578
QString sizeofTypeExpression(const QString &type, QtDumperHelper::Debugger debugger)
579
580
581
{
    if (type.endsWith(QLatin1Char('*')))
        return QLatin1String("sizeof(void*)");
582
    if (debugger != QtDumperHelper::GdbDebugger || type.endsWith(QLatin1Char('>')))
583
584
585
586
        return QLatin1String("sizeof(") + type + QLatin1Char(')');
    return QLatin1String("sizeof(") + gdbQuoteTypes(type) + QLatin1Char(')');
}

587
588
// Utilities to decode string data returned by the dumper helpers.

589
QString quoteUnprintableLatin1(const QByteArray &ba)
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
{
    QString res;
    char buf[10];
    for (int i = 0, n = ba.size(); i != n; ++i) {
        const unsigned char c = ba.at(i);
        if (isprint(c)) {
            res += c;
        } else {
            qsnprintf(buf, sizeof(buf) - 1, "\\%x", int(c));
            res += buf;
        }
    }
    return res;
}

hjk's avatar
hjk committed
605
QString decodeData(const QByteArray &ba, int encoding)
606
607
608
{
    switch (encoding) {
        case 0: // unencoded 8 bit data
hjk's avatar
hjk committed
609
            return quoteUnprintableLatin1(ba);
610
611
612
        case 1: { //  base64 encoded 8 bit data, used for QByteArray
            const QChar doubleQuote(QLatin1Char('"'));
            QString rc = doubleQuote;
hjk's avatar
hjk committed
613
            rc += quoteUnprintableLatin1(QByteArray::fromBase64(ba));
614
615
616
617
618
            rc += doubleQuote;
            return rc;
        }
        case 2: { //  base64 encoded 16 bit data, used for QString
            const QChar doubleQuote(QLatin1Char('"'));
619
            const QByteArray decodedBa = QByteArray::fromBase64(ba);
620
            QString rc = doubleQuote;
621
622
            rc += QString::fromUtf16(reinterpret_cast<const ushort *>
                (decodedBa.data()), decodedBa.size() / 2);
623
624
625
626
            rc += doubleQuote;
            return rc;
        }
        case 3: { //  base64 encoded 32 bit data
627
            const QByteArray decodedBa = QByteArray::fromBase64(ba);
628
629
            const QChar doubleQuote(QLatin1Char('"'));
            QString rc = doubleQuote;
630
631
            rc += QString::fromUcs4(reinterpret_cast<const uint *>
                (decodedBa.data()), decodedBa.size() / 4);
632
633
634
635
            rc += doubleQuote;
            return rc;
        }
        case 4: { //  base64 encoded 16 bit data, without quotes (see 2)
636
            const QByteArray decodedBa = QByteArray::fromBase64(ba);
637
638
            return QString::fromUtf16(reinterpret_cast<const ushort *>
                (decodedBa.data()), decodedBa.size() / 2);
639
        }
640
641
642
        case 5: { //  base64 encoded 8 bit data, without quotes (see 1)
            return quoteUnprintableLatin1(QByteArray::fromBase64(ba));
        }
hjk's avatar
hjk committed
643
644
645
646
647
648
        case 6: { //  %02x encoded 8 bit data
            const QChar doubleQuote(QLatin1Char('"'));
            const QByteArray decodedBa = QByteArray::fromHex(ba);
            //qDebug() << quoteUnprintableLatin1(decodedBa) << "\n\n";
            return doubleQuote + QString::fromLatin1(decodedBa) + doubleQuote;
        }
649
        case 7: { //  %04x encoded 16 bit data, Little Endian
650
            const QChar doubleQuote(QLatin1Char('"'));
651
            const QByteArray decodedBa = QByteArray::fromHex(ba);
hjk's avatar
hjk committed
652
            //qDebug() << quoteUnprintableLatin1(decodedBa) << "\n\n";
653
654
            return doubleQuote + QString::fromUtf16(reinterpret_cast<const ushort *>
                (decodedBa.data()), decodedBa.size() / 2) + doubleQuote;
655
        }
656
657
658
659
660
661
662
        case 8: { //  %08x encoded 32 bit data, Little Endian
            const QChar doubleQuote(QLatin1Char('"'));
            const QByteArray decodedBa = QByteArray::fromHex(ba);
            //qDebug() << quoteUnprintableLatin1(decodedBa) << "\n\n";
            return doubleQuote + QString::fromUcs4(reinterpret_cast<const uint *>
                (decodedBa.data()), decodedBa.size() / 4) + doubleQuote;
        }
663
    }
hjk's avatar
hjk committed
664
    qDebug() << "ENCODING ERROR: " << encoding;
665
666
667
    return QCoreApplication::translate("Debugger", "<Encoding error>");
}

668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
// Editor tooltip support
bool isCppEditor(Core::IEditor *editor)
{
    static QStringList cppMimeTypes;
    if (cppMimeTypes.empty()) {
        cppMimeTypes << QLatin1String(CppTools::Constants::C_SOURCE_MIMETYPE)
                << QLatin1String(CppTools::Constants::CPP_SOURCE_MIMETYPE)
                << QLatin1String(CppTools::Constants::CPP_HEADER_MIMETYPE)
                << QLatin1String(CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE);
    }
    if (const Core::IFile *file = editor->file())
        return cppMimeTypes.contains(file->mimeType());
    return  false;
}

// Find the function the cursor is in to use a scope.


686

687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719


// Return the Cpp expression, and, if desired, the function
QString cppExpressionAt(TextEditor::ITextEditor *editor, int pos,
                        int *line, int *column, QString *function /* = 0 */)
{

    *line = *column = 0;
    if (function)
        function->clear();

    const QPlainTextEdit *plaintext = qobject_cast<QPlainTextEdit*>(editor->widget());
    if (!plaintext)
        return QString();

    QString expr = plaintext->textCursor().selectedText();
    if (expr.isEmpty()) {
        QTextCursor tc(plaintext->document());
        tc.setPosition(pos);

        const QChar ch = editor->characterAt(pos);
        if (ch.isLetterOrNumber() || ch == QLatin1Char('_'))
            tc.movePosition(QTextCursor::EndOfWord);

        // Fetch the expression's code.
        CPlusPlus::ExpressionUnderCursor expressionUnderCursor;
        expr = expressionUnderCursor(tc);
        *column = tc.columnNumber();
        *line = tc.blockNumber();
    } else {
        const QTextCursor tc = plaintext->textCursor();
        *column = tc.columnNumber();
        *line = tc.blockNumber();
720
    }
721
722
723
724
725
726
727
728
729

    if (function && !expr.isEmpty())
        if (const Core::IFile *file = editor->file())
            if (CppTools::CppModelManagerInterface *modelManager = ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>())
                *function = CppTools::AbstractEditorSupport::functionAt(modelManager, file->fileName(), *line, *column);

    return expr;
}

730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
// ----------------- QtDumperHelper::TypeData
QtDumperHelper::TypeData::TypeData() :
    type(UnknownType),
    isTemplate(false)
{
}

void QtDumperHelper::TypeData::clear()
{
    isTemplate = false;
    type = UnknownType;
    tmplate.clear();
    inner.clear();
}

// ----------------- QtDumperHelper
QtDumperHelper::QtDumperHelper() :
747
748
    m_qtVersion(0),
    m_dumperVersion(1.0)
749
{
750
    qFill(m_specialSizes, m_specialSizes + SpecialSizeCount, 0);
751
    setQClassPrefixes(QString());
752
753
754
755
756
757
}

void QtDumperHelper::clear()
{
    m_nameTypeMap.clear();
    m_qtVersion = 0;
758
    m_dumperVersion = 1.0;
759
    m_qtNamespace.clear();
760
    m_sizeCache.clear();
761
762
    qFill(m_specialSizes, m_specialSizes + SpecialSizeCount, 0);
    m_expressionCache.clear();
763
764
765
766
767
768
    setQClassPrefixes(QString());
}

QString QtDumperHelper::msgDumperOutdated(double requiredVersion, double currentVersion)
{
    return QCoreApplication::translate("QtDumperHelper",
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
769
                                       "Found an outdated version of the debugging helper library (%1); version %2 is required.").
770
                                       arg(currentVersion).arg(requiredVersion);
771
772
773
774
775
776
777
778
779
780
781
782
783
784
}

static inline void formatQtVersion(int v, QTextStream &str)
{
    str  << ((v >> 16) & 0xFF) << '.' << ((v >> 8) & 0xFF) << '.' << (v & 0xFF);
}

QString QtDumperHelper::toString(bool debug) const
{
    if (debug)  {
        QString rc;
        QTextStream str(&rc);
        str << "version=";
        formatQtVersion(m_qtVersion, str);
785
        str << "dumperversion='" << m_dumperVersion <<  "' namespace='" << m_qtNamespace << "'," << m_nameTypeMap.size() << " known types <type enum>: ";
786
787
788
789
        const NameTypeMap::const_iterator cend = m_nameTypeMap.constEnd();
        for (NameTypeMap::const_iterator it = m_nameTypeMap.constBegin(); it != cend; ++it) {
            str <<",[" << it.key() << ',' << it.value() << ']';
        }
790
791
792
793
        str << "\nSpecial size: ";
        for (int i = 0; i < SpecialSizeCount; i++)
            str << ' ' << m_specialSizes[i];
        str << "\nSize cache: ";
794
795
        const SizeCache::const_iterator scend = m_sizeCache.constEnd();
        for (SizeCache::const_iterator it = m_sizeCache.constBegin(); it != scend; ++it) {
Friedemann Kleint's avatar
Friedemann Kleint committed
796
            str << ' ' << it.key() << '=' << it.value() << '\n';
797
        }
798
799
800
801
        str << "\nExpression cache: (" << m_expressionCache.size() << ")\n";
        const QMap<QString, QString>::const_iterator excend = m_expressionCache.constEnd();
        for (QMap<QString, QString>::const_iterator it = m_expressionCache.constBegin(); it != excend; ++it)
            str << "    " << it.key() << ' ' << it.value() << '\n';
802
803
804
805
        return rc;
    }
    const QString nameSpace = m_qtNamespace.isEmpty() ? QCoreApplication::translate("QtDumperHelper", "<none>") : m_qtNamespace;
    return QCoreApplication::translate("QtDumperHelper",
806
                                       "%n known types, Qt version: %1, Qt namespace: %2 Dumper version: %3",
807
                                       0, QCoreApplication::CodecForTr,
808
                                       m_nameTypeMap.size()).arg(qtVersionString(), nameSpace).arg(m_dumperVersion);
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
}

QtDumperHelper::Type QtDumperHelper::simpleType(const QString &simpleType) const
{
    return m_nameTypeMap.value(simpleType, UnknownType);
}

int QtDumperHelper::qtVersion() const
{
    return m_qtVersion;
}

QString QtDumperHelper::qtNamespace() const
{
    return m_qtNamespace;
}

int QtDumperHelper::typeCount() const
{
    return m_nameTypeMap.size();
}

// Look up unnamespaced 'std' types.
static inline QtDumperHelper::Type stdType(const QString &s)
{
    if (s == QLatin1String("vector"))
        return QtDumperHelper::StdVectorType;
    if (s == QLatin1String("deque"))
        return QtDumperHelper::StdDequeType;
    if (s == QLatin1String("set"))
        return QtDumperHelper::StdSetType;
    if (s == QLatin1String("stack"))
        return QtDumperHelper::StdStackType;
    if (s == QLatin1String("map"))
        return QtDumperHelper::StdMapType;
    if (s == QLatin1String("basic_string"))
        return QtDumperHelper::StdStringType;
    return QtDumperHelper::UnknownType;
}

QtDumperHelper::Type QtDumperHelper::specialType(QString s)
{
    // Std classes.
    if (s.startsWith(QLatin1String("std::")))
        return stdType(s.mid(5));
    // Strip namespace
855
    // FIXME: that's not a good idea as it makes all namespaces equal.
856
857
858
859
860
861
862
    const int namespaceIndex = s.lastIndexOf(QLatin1String("::"));
    if (namespaceIndex == -1) {
        // None ... check for std..
        const Type sType = stdType(s);
        if (sType != UnknownType)
            return sType;
    } else {
863
        s = s.mid(namespaceIndex + 2);
864
    }
865
866
    if (s == QLatin1String("QAbstractItem"))
        return QAbstractItemType;
867
868
869
870
    if (s == QLatin1String("QMap"))
        return QMapType;
    if (s == QLatin1String("QMapNode"))
        return QMapNodeType;
hjk's avatar
hjk committed
871
872
873
874
875
876
877
878
879
880
881
882
883
884
    if (s == QLatin1String("QMultiMap"))
        return QMultiMapType;
    if (s == QLatin1String("QObject"))
        return QObjectType;
    if (s == QLatin1String("QObjectSignal"))
        return QObjectSignalType;
    if (s == QLatin1String("QObjectSlot"))
        return QObjectSlotType;
    if (s == QLatin1String("QStack"))
        return QStackType;
    if (s == QLatin1String("QVector"))
        return QVectorType;
    if (s == QLatin1String("QWidget"))
        return QWidgetType;
885
886
887
888
889
890
891
892
893
894
895
896
    return UnknownType;
}

QString QtDumperHelper::qtVersionString() const
{
    QString rc;
    QTextStream str(&rc);
    formatQtVersion(m_qtVersion, str);
    return rc;
}

// Parse a list of types.
897
void QtDumperHelper::parseQueryTypes(const QStringList &l, Debugger  /* debugger */)
898
899
900
901
902
{
    m_nameTypeMap.clear();
    const int count = l.count();
    for (int i = 0; i < count; i++) {
        const Type t = specialType(l.at(i));
903
        m_nameTypeMap.insert(l.at(i), t != UnknownType ? t : SupportedType);
904
905
906
    }
}

907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
static inline QString qClassName(const QString &qtNamespace, const char *className)
{
    if (qtNamespace.isEmpty())
        return QString::fromAscii(className);
    QString rc = qtNamespace;
    rc += QLatin1String("::");
    rc += QString::fromAscii(className);
    return rc;
}

void QtDumperHelper::setQClassPrefixes(const QString &qNamespace)
{
    // Prefixes with namespaces
    m_qPointerPrefix = qClassName(qNamespace, "QPointer");
    m_qSharedPointerPrefix = qClassName(qNamespace, "QSharedPointer");
    m_qSharedDataPointerPrefix = qClassName(qNamespace, "QSharedDataPointer");
    m_qWeakPointerPrefix = qClassName(qNamespace, "QWeakPointer");
924
925
926
    m_qListPrefix = qClassName(qNamespace, "QList");
    m_qLinkedListPrefix = qClassName(qNamespace, "QLinkedList");
    m_qVectorPrefix = qClassName(qNamespace, "QVector");
Friedemann Kleint's avatar
Friedemann Kleint committed
927
    m_qQueuePrefix = qClassName(qNamespace, "QQueue");
928
929
}

930
static inline double getDumperVersion(const GdbMi &contents)
931
{
932
933
    const GdbMi dumperVersionG = contents.findChild("dumperversion");
    if (dumperVersionG.type() != GdbMi::Invalid) {
934
        bool ok;
935
        const double v = QString::fromAscii(dumperVersionG.data()).toDouble(&ok);
936
        if (ok)
937
            return v;
938
    }
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
    return 1.0;
}

bool QtDumperHelper::parseQuery(const GdbMi &contents, Debugger debugger)
{
    clear();
    if (debug > 1)
        qDebug() << "parseQuery" << contents.toString(true, 2);

    // Common info, dumper version, etc
    m_qtNamespace = QLatin1String(contents.findChild("namespace").data());
    int qtv = 0;
    const GdbMi qtversion = contents.findChild("qtversion");
    if (qtversion.children().size() == 3) {
        qtv = (qtversion.childAt(0).data().toInt() << 16)
                    + (qtversion.childAt(1).data().toInt() << 8)
                    + qtversion.childAt(2).data().toInt();
    }
    m_qtVersion = qtv;
    // Get list of helpers
    QStringList availableSimpleDebuggingHelpers;
    foreach (const GdbMi &item, contents.findChild("dumpers").children())
        availableSimpleDebuggingHelpers.append(QLatin1String(item.data()));
    parseQueryTypes(availableSimpleDebuggingHelpers, debugger);
    m_dumperVersion = getDumperVersion(contents);
    // Parse sizes
    foreach (const GdbMi &sizesList, contents.findChild("sizes").children()) {
        const int childCount = sizesList.childCount();
        if (childCount > 1) {
            const int size = sizesList.childAt(0).data().toInt();
            for (int c = 1; c < childCount; c++)
                addSize(QLatin1String(sizesList.childAt(c).data()), size);
        }
    }
    // Parse expressions
    foreach (const GdbMi &exprList, contents.findChild("expressions").children())
        if (exprList.childCount() == 2)
            m_expressionCache.insert(QLatin1String(exprList.childAt(0).data()),
                                     QLatin1String(exprList.childAt(1).data()));
978
979
980
    return true;
}

981
982
// parse a query
bool QtDumperHelper::parseQuery(const char *data, Debugger debugger)
983
{
984
985
    GdbMi root;
    root.fromStringMultiple(QByteArray(data));
986
987
988
    if (!root.isValid())
        return false;
    return parseQuery(root, debugger);
989
990
}

991
992
993
void QtDumperHelper::addSize(const QString &name, int size)
{
    // Special interest cases
994
995
996
997
998
999
1000
1001
1002
    if (name == QLatin1String("char*")) {
        m_specialSizes[PointerSize] = size;
        return;
    }
    const SpecialSizeType st = specialSizeType(name);
    if (st != SpecialSizeCount) {
        m_specialSizes[st] = size;
        return;
    }
1003
    do {
1004
        // CDB helpers
1005
        if (name == QLatin1String("std::string")) {
1006
1007
            m_sizeCache.insert(QLatin1String("std::basic_string<char,std::char_traits<char>,std::allocator<char> >"), size);
            m_sizeCache.insert(QLatin1String("basic_string<char,char_traits<char>,allocator<char> >"), size);
1008
1009
1010
            break;
        }
        if (name == QLatin1String("std::wstring")) {
1011
1012
            m_sizeCache.insert(QLatin1String("basic_string<unsigned short,char_traits<unsignedshort>,allocator<unsignedshort> >"), size);
            m_sizeCache.insert(QLatin1String("std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >"), size);
1013
1014
1015
1016
1017
1018
            break;
        }
    } while (false);
    m_sizeCache.insert(name, size);
}

1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
QtDumperHelper::Type QtDumperHelper::type(const QString &typeName) const
{
    const QtDumperHelper::TypeData td = typeData(typeName);
    return td.type;
}

QtDumperHelper::TypeData QtDumperHelper::typeData(const QString &typeName) const
{
    TypeData td;
    td.type = UnknownType;
    const Type st = simpleType(typeName);
    if (st != UnknownType) {
        td.isTemplate = false;
1032
        td.type = st;
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
        return td;
    }
    // Try template
    td.isTemplate = extractTemplate(typeName, &td.tmplate, &td.inner);
    if (!td.isTemplate)
        return td;
    // Check the template type QMap<X,Y> -> 'QMap'
    td.type = simpleType(td.tmplate);
    return td;
}

1044
1045
1046
// Format an expression to have the debugger query the
// size. Use size cache if possible
QString QtDumperHelper::evaluationSizeofTypeExpression(const QString &typeName,
1047
                                                       Debugger debugger) const
1048
{
1049
1050
1051
1052
1053
1054
1055
    // 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
1056
1057
1058
1059
    const SizeCache::const_iterator sit = m_sizeCache.constFind(typeName);
    if (sit != m_sizeCache.constEnd())
        return QString::number(sit.value());
    // Finally have the debugger evaluate
1060
    return sizeofTypeExpression(typeName, debugger);
1061
1062
}

1063
QtDumperHelper::SpecialSizeType QtDumperHelper::specialSizeType(const QString &typeName) const
1064
1065
1066
1067
1068
1069
1070
1071
1072
{
    if (isPointerType(typeName))
        return PointerSize;
    static const QString intType = QLatin1String("int");
    static const QString stdAllocatorPrefix = QLatin1String("std::allocator");
    if (typeName == intType)
        return IntSize;
    if (typeName.startsWith(stdAllocatorPrefix))
        return StdAllocatorSize;
1073
    if (typeName.startsWith(m_qPointerPrefix))
1074
        return QPointerSize;
1075
    if (typeName.startsWith(m_qSharedPointerPrefix))
1076
        return QSharedPointerSize;
1077
    if (typeName.startsWith(m_qSharedDataPointerPrefix))
1078
        return QSharedDataPointerSize;
1079
    if (typeName.startsWith(m_qWeakPointerPrefix))
1080
        return QWeakPointerSize;
1081
1082
1083
1084
1085
1086
1087
1088
    if (typeName.startsWith(m_qListPrefix))
        return QListSize;
    if (typeName.startsWith(m_qLinkedListPrefix))
        return QLinkedListSize;
    if (typeName.startsWith(m_qVectorPrefix))
        return QVectorSize;
    if (typeName.startsWith(m_qQueuePrefix))
        return QQueueSize;
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
    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;
}

1103
1104
void QtDumperHelper::evaluationParameters(const WatchData &data,
                                          const TypeData &td,
1105
                                          Debugger debugger,
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
                                          QByteArray *inBuffer,
                                          QStringList *extraArgsIn) const
{
    enum { maxExtraArgCount = 4 };

    QStringList &extraArgs = *extraArgsIn;

    // See extractTemplate for parameters
    QStringList inners = td.inner.split(QLatin1Char('@'));
    if (inners.at(0).isEmpty())
        inners.clear();
    for (int i = 0; i != inners.size(); ++i)
        inners[i] = inners[i].simplified();

    QString outertype = td.isTemplate ? td.tmplate : data.type;
    // adjust the data extract
    if (outertype == m_qtNamespace + QLatin1String("QWidget"))
        outertype = m_qtNamespace + QLatin1String("QObject");

1125
1126
    QString inner = td.inner;

1127
1128
1129
1130
1131
1132
1133
    extraArgs.clear();

    if (!inners.empty()) {
        // "generic" template dumpers: passing sizeof(argument)
        // gives already most information the dumpers need
        const int count = qMin(int(maxExtraArgCount), inners.size());
        for (int i = 0; i < count; i++)
1134
            extraArgs.push_back(evaluationSizeofTypeExpression(inners.at(i), debugger));
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
    }
    int extraArgCount = extraArgs.size();
    // Pad with zeros
    const QString zero = QString(QLatin1Char('0'));
    const int extraPad = maxExtraArgCount - extraArgCount;
    for (int i = 0; i < extraPad; i++)
        extraArgs.push_back(zero);

    // in rare cases we need more or less:
    switch (td.type) {
1145
1146
1147
    case QAbstractItemType:
        inner = data.addr.mid(1);
        break;
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
    case QObjectSlotType:
    case QObjectSignalType: {
            // we need the number out of something like
            // iname="local.ob.slots.2" // ".deleteLater()"?
            const int pos = data.iname.lastIndexOf('.');
            const QString slotNumber = data.iname.mid(pos + 1);
            QTC_ASSERT(slotNumber.toInt() != -1, /**/);
            extraArgs[0] = slotNumber;
        }
        break;
    case QMapType:
    case QMultiMapType: {
            QString nodetype;
1161
            if (m_qtVersion >= 0x040500) {
1162
1163
                nodetype = m_qtNamespace + QLatin1String("QMapNode");
                nodetype += data.type.mid(outertype.size());
1164
            } else {
1165
1166
                // FIXME: doesn't work for QMultiMap
                nodetype  = data.type + QLatin1String("::Node");
1167
            }
1168
1169
            //qDebug() << "OUTERTYPE: " << outertype << " NODETYPE: " << nodetype
            //    << "QT VERSION" << m_qtVersion << ((4 << 16) + (5 << 8) + 0);
1170
            extraArgs[2] = evaluationSizeofTypeExpression(nodetype, debugger);
1171
            extraArgs[3] = qMapNodeValueOffsetExpression(nodetype, data.addr, debugger);
1172
        }
1173
        break;
1174
    case QMapNodeType:
1175
        extraArgs[2] = evaluationSizeofTypeExpression(data.type, debugger);
1176
        extraArgs[3] = qMapNodeValueOffsetExpression(data.type, data.addr, debugger);
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
        break;
    case StdVectorType:
        //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
        if (inners.at(0) == QLatin1String("bool")) {
            outertype = QLatin1String("std::vector::bool");
        }
        break;
    case StdDequeType:
        extraArgs[1] = zero;
    case StdStackType:
        // remove 'std::allocator<...>':
        extraArgs[1] = zero;
        break;
    case StdSetType:
        // remove 'std::less<...>':
        extraArgs[1] = zero;
        // remove 'std::allocator<...>':
        extraArgs[2] = zero;
        break;
    case StdMapType: {
1197
            // We need the offset of the second item in the value pair.
1198
            // We read the type of the pair from the allocator argument because
1199
            // that gets the constness "right" (in the sense that gdb/cdb can
1200
1201
1202
            // read it back: "std::allocator<std::pair<Key,Value> >"
            // -> "std::pair<Key,Value>". Different debuggers have varying
            // amounts of terminating blanks...
1203
1204
            extraArgs[2].clear();
            extraArgs[3] = zero;
1205
            QString pairType = inners.at(3);
1206
1207
1208
            int bracketPos = pairType.indexOf(QLatin1Char('<'));
            if (bracketPos != -1)
                pairType.remove(0, bracketPos + 1);
1209
            // We don't want the comparator and the allocator confuse gdb.
1210
1211
1212
1213
            const QChar closingBracket = QLatin1Char('>');
            bracketPos = pairType.lastIndexOf(closingBracket);
            if (bracketPos != -1)
                bracketPos = pairType.lastIndexOf(closingBracket, bracketPos - pairType.size() - 1);
1214
1215
            if (bracketPos != -1)
                pairType.truncate(bracketPos + 1);
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
            if (debugger == GdbDebugger) {
                extraArgs[2] = QLatin1String("(size_t)&(('");
                extraArgs[2] += pairType;
                extraArgs[2] += QLatin1String("'*)0)->second");
            } else {
                // Cdb: The std::pair is usually in scope. Still, this expression
                // occasionally fails for complex types (std::string).
                // We need an address as CDB cannot do the 0-trick.
                // Use data address or try at least cache if missing.
                const QString address = data.addr.isEmpty() ? QString::fromLatin1("DUMMY_ADDRESS") : data.addr;
                QString offsetExpr;
                QTextStream str(&offsetExpr);
                str << "(size_t)&(((" << pairType << " *)" << address << ")->second)" << '-' << address;
                extraArgs[2] = lookupCdbDummyAddressExpression(offsetExpr, address);
            }
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
        }
        break;
    case StdStringType:
        //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
        if (inners.at(0) == QLatin1String("char")) {
            outertype = QLatin1String("std::string");
        } else if (inners.at(0) == QLatin1String("wchar_t")) {
            outertype = QLatin1String("std::wstring");
        }
        qFill(extraArgs, zero);
        break;
    case UnknownType:
        qWarning("Unknown type encountered in %s.\n", Q_FUNC_INFO);
        break;
    case SupportedType:
1246
    case QVectorType:
hjk's avatar
hjk committed
1247
    case QStackType:
1248
1249
    case QObjectType:
    case QWidgetType:
1250
1251
1252
        break;
    }

1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
    // 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();
            }
        }
    }

1267
1268
1269
    inBuffer->clear();
    inBuffer->append(outertype.toUtf8());
    inBuffer->append('\0');
1270
    inBuffer->append(data.iname);
1271
    inBuffer->append('\0');
1272
    inBuffer->append(data.exp);
1273
    inBuffer->append('\0');
1274
    inBuffer->append(inner.toUtf8());
1275
    inBuffer->append('\0');
1276
    inBuffer->append(data.iname);
1277
    inBuffer->append('\0');
1278
1279
1280
1281
1282

    if (debug)
        qDebug() << '\n' << Q_FUNC_INFO << '\n' << data.toString() << "\n-->" << outertype << td.type << extraArgs;
}

1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
// Return debugger expression to get the offset of a map node.
QString QtDumperHelper::qMapNodeValueOffsetExpression(const QString &type,
                                                      const QString &addressIn,
                                                      Debugger debugger) const
{
    switch (debugger) {
    case GdbDebugger:
        return QLatin1String("(size_t)&(('") + type + QLatin1String("'*)0)->value");
    case CdbDebugger: {
            // Cdb: This will only work if a QMapNode is in scope.
            // We need an address as CDB cannot do the 0-trick.
            // Use data address or try at least cache if missing.
            const QString address = addressIn.isEmpty() ? QString::fromLatin1("DUMMY_ADDRESS") : addressIn;
            QString offsetExpression;
            QTextStream(&offsetExpression) << "(size_t)&(((" << type
                    << " *)" << address << ")->value)-" << address;
            return lookupCdbDummyAddressExpression(offsetExpression, address);
        }
    }
    return QString();
}

/* Cdb cannot do tricks like ( "&(std::pair<int,int>*)(0)->second)",
 * that is, use a null pointer to determine the offset of a member.
 * It tries to dereference the address at some point and fails with
 * "memory access error". As a trick, use the address of the watch item
 * to do this. However, in the expression cache, 0 is still used, so,
 * for cache lookups,  use '0' as address. */
QString QtDumperHelper::lookupCdbDummyAddressExpression(const QString &expr,
                                                        const QString &address) const
{
    QString nullExpr = expr;
    nullExpr.replace(address, QString(QLatin1Char('0')));
    const QString rc = m_expressionCache.value(nullExpr, expr);
    if (debug)
        qDebug() << "lookupCdbDummyAddressExpression" << expr << rc;
    return rc;
}

1322
// GdbMi parsing helpers for parsing dumper value results
1323

1324
1325
1326
static bool gdbMiGetIntValue(int *target,
                             const GdbMi &node,
                             const char *child)
1327
{
1328
1329
1330
1331
1332
1333
1334
    *target = -1;
    const GdbMi childNode = node.findChild(child);
    if (!childNode.isValid())
        return false;
    bool ok;
    *target = childNode.data().toInt(&ok);
    return ok;
1335
1336
}

1337
1338
1339
1340
1341
1342
// Find a string child node and assign value if it exists.
// Optionally decode.
static bool gdbMiGetStringValue(QString *target,
                             const GdbMi &node,
                             const char *child,
                             const char *encodingChild = 0)
1343
{
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
    target->clear();
    const GdbMi childNode = node.findChild(child);
    if (!childNode.isValid())
        return false;
    // Encoded data
    if (encodingChild) {
        int encoding;
        if (!gdbMiGetIntValue(&encoding, node, encodingChild))
            encoding = 0;
        *target = decodeData(childNode.data(), encoding);
        return true;
1355
    }
1356
1357
    // Plain data
    *target = QLatin1String(childNode.data());
1358
1359
1360
    return true;
}

1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
static bool gdbMiGetByteArrayValue(QByteArray *target,
                             const GdbMi &node,
                             const char *child,
                             const char *encodingChild = 0)
{
    QString str;
    const bool success = gdbMiGetStringValue(&str, node, child, encodingChild);
    *target = str.toLatin1();
    return success;
}

1372
1373
1374
static bool gdbMiGetBoolValue(bool *target,
                             const GdbMi &node,
                             const char *child)
1375
{
1376
1377
1378
1379
1380
    *target = false;
    const GdbMi childNode = node.findChild(child);
    if (!childNode.isValid())
        return false;
    *target = childNode.data() == "true";
1381
1382
1383
    return true;
}

1384
1385
1386
1387
/* Context to store parameters that influence the next level children.
 *  (next level only, it is not further inherited). For example, the root item
 * can provide a "childtype" node that specifies the type of the children. */

1388
1389
struct GdbMiRecursionContext
{
1390
1391
1392
1393
1394
1395
1396
    GdbMiRecursionContext(int recursionLevelIn = 0) :
            recursionLevel(recursionLevelIn), childNumChild(-1), childIndex(0) {}

    int recursionLevel;
    int childNumChild;
    int childIndex;
    QString childType;
1397
    QByteArray parentIName;
1398
1399
1400
1401
1402
};

static void gbdMiToWatchData(const GdbMi &root,
                             const GdbMiRecursionContext &ctx,
                             QList<WatchData> *wl)
1403
{
1404
1405
    if (debug > 1)
        qDebug() << Q_FUNC_INFO << '\n' << root.toString(false, 0);
1406
    WatchData w;
1407
    QString v;
1408
    QByteArray b;
1409
1410
1411
    // Check for name/iname and use as expression default
    if (ctx.recursionLevel == 0) {
        // parents have only iname, from which name is derived
1412
1413
        QString iname;
        if (!gdbMiGetStringValue(&iname, root, "iname"))
1414
            qWarning("Internal error: iname missing");
1415
1416
        w.iname = iname.toLatin1();
        w.name = iname;
1417
1418
1419
        const int lastDotPos = w.name.lastIndexOf(QLatin1Char('.'));
        if (lastDotPos != -1)
            w.name.remove(0, lastDotPos + 1);