qttypes.py 100 KB
Newer Older
hjk's avatar
hjk committed
1
2
3
4
5
6
7

#######################################################################
#
# Dumper Implementations
#
#######################################################################

8
9
from __future__ import with_statement

10
11
12
13
14
15
16
17
18
19
movableTypes = set([
    "QBrush", "QBitArray", "QByteArray", "QCustomTypeInfo", "QChar", "QDate",
    "QDateTime", "QFileInfo", "QFixed", "QFixedPoint", "QFixedSize",
    "QHashDummyValue", "QIcon", "QImage", "QLine", "QLineF", "QLatin1Char",
    "QLocale", "QMatrix", "QModelIndex", "QPoint", "QPointF", "QPen",
    "QPersistentModelIndex", "QResourceRoot", "QRect", "QRectF", "QRegExp",
    "QSize", "QSizeF", "QString", "QTime", "QTextBlock", "QUrl", "QVariant",
    "QXmlStreamAttribute", "QXmlStreamNamespaceDeclaration",
    "QXmlStreamNotationDeclaration", "QXmlStreamEntityDeclaration"
])
20

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
def checkSimpleRef(ref):
    count = int(ref["_q_value"])
    check(count > 0)
    check(count < 1000000)

def checkRef(ref):
    try:
        count = int(ref["atomic"]["_q_value"]) # Qt 5.
        minimum = -1
    except:
        count = int(ref["_q_value"]) # Qt 4.
        minimum = 0
    # Assume there aren't a million references to any object.
    check(count >= minimum)
    check(count < 1000000)


hjk's avatar
hjk committed
38
39
40
41
def qByteArrayDataData(d, value):
    checkRef(value['ref'])
    size = int(value['size'])
    alloc = int(value['alloc'])
42
43
    try:
        # Qt 5. Will fail on Qt 4 due to the missing 'offset' member.
hjk's avatar
hjk committed
44
        addr = d.addressOf(value) + int(value['offset'])
45
46
    except:
        # Qt 4:
hjk's avatar
hjk committed
47
        addr = value['data']
hjk's avatar
hjk committed
48
    return createPointerValue(value, addr, d.charType()), size, alloc
49

hjk's avatar
hjk committed
50
51
def qByteArrayData(d, value):
    return d.byteArrayDataData(value['d'].dereference())
52
53

def qEncodeByteArray(d, value, limit = None):
hjk's avatar
hjk committed
54
    data, size, alloc = d.byteArrayData(value)
55
56
57
58
59
60
61
62
63
    if alloc != 0:
        check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
    limit = d.computeLimit(size, limit)
    s = d.readRawMemory(data, limit)
    if limit < size:
        s += "2e2e2e"
    return s

def qEncodeString(d, value, limit = 0):
hjk's avatar
hjk committed
64
    data, size, alloc = d.stringData(value)
65
66
67
68
69
70
71
72
    if alloc != 0:
        check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
    limit = d.computeLimit(size, limit)
    s = d.readRawMemory(data, 2 * limit)
    if limit < size:
        s += "2e002e002e00"
    return s

hjk's avatar
hjk committed
73
74
75
76
77
78
79
80
def mapForms():
    return "Normal,Compact"

def arrayForms():
    if hasPlot():
        return "Normal,Plot"
    return "Normal"

hjk's avatar
hjk committed
81
def qMapCompact(format, keyType, valueType):
hjk's avatar
hjk committed
82
83
84
85
    if format == 2:
        return True # Compact.
    return isSimpleType(keyType) and isSimpleType(valueType)

hjk's avatar
hjk committed
86
def qPutMapName(d, value):
hjk's avatar
hjk committed
87
88
89
90
91
92
93
94
95
96
97
98
99
100
    if str(value.type) == d.ns + "QString":
        d.put('key="%s",' % d.encodeString(value))
        d.put('keyencoded="%s",' % Hex4EncodedLittleEndian)
    elif str(value.type) == d.ns + "QByteArray":
        d.put('key="%s",' % d.encodeByteArray(value))
        d.put('keyencoded="%s",' % Hex2EncodedLatin1)
    else:
        if lldbLoaded:
            d.put('name="%s",' % value.GetValue())
        else:
            d.put('name="%s",' % value)

Dumper.encodeByteArray = qEncodeByteArray
Dumper.byteArrayData = qByteArrayData
hjk's avatar
hjk committed
101
102
103
104
105
106
Dumper.byteArrayDataData = qByteArrayDataData
Dumper.putByteArrayValue = \
    lambda d, value: d.putValue(d.encodeByteArray(value), Hex2EncodedLatin1)
Dumper.putStringValue = \
    lambda d, value: d.putValue(d.encodeString(value), Hex4EncodedLittleEndian)

hjk's avatar
hjk committed
107
Dumper.encodeString = qEncodeString
hjk's avatar
hjk committed
108
Dumper.stringData = Dumper.byteArrayData
hjk's avatar
hjk committed
109
Dumper.putMapName = qPutMapName
hjk's avatar
hjk committed
110
111

Dumper.isMapCompact = \
hjk's avatar
hjk committed
112
    lambda d, keyType, valueType: qMapCompact(d.currentItemFormat(), keyType, valueType)
hjk's avatar
hjk committed
113
114


hjk's avatar
hjk committed
115
# Returns True when it encounters a QObject or derived class.
hjk's avatar
hjk committed
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
def tryPutObjectNameValue(d, value):
    try:
        # Is this derived from QObject?
        dd = value["d_ptr"]["d"]
        privateType = d.lookupType(d.ns + "QObjectPrivate")
        staticMetaObject = value["staticMetaObject"]
        d_ptr = dd.cast(privateType.pointer()).dereference()
        objectName = None
        try:
            objectName = d_ptr["objectName"]
        except: # Qt 5
            p = d_ptr["extraData"]
            if not isNull(p):
                objectName = p.dereference()["objectName"]
        if not objectName is None:
            data, size, alloc = d.stringData(objectName)
            if size > 0:
                str = d.readRawMemory(data, 2 * size)
                d.putValue(str, Hex4EncodedLittleEndian, 1)
hjk's avatar
hjk committed
135
        return True
hjk's avatar
hjk committed
136
    except:
hjk's avatar
hjk committed
137
        return False
hjk's avatar
hjk committed
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

Dumper.tryPutObjectNameValue = tryPutObjectNameValue

############################################################################################


def qdump__QAtomicInt(d, value):
    d.putValue(value["_q_value"])
    d.putNumChild(0)


def qdump__QBasicAtomicInt(d, value):
    d.putValue(value["_q_value"])
    d.putNumChild(0)


def qdump__QBasicAtomicPointer(d, value):
    d.putType(value.type)
    p = cleanAddress(value["_q_value"])
    d.putValue(p)
    d.putPointerValue(value.address)
    d.putNumChild(p)
    if d.isExpanded():
        with Children(d):
           d.putItem(value["_q_value"])

hjk's avatar
hjk committed
164
165
166
167
168
169
170
def qdump__QByteArrayData(d, value):
    data, size, alloc = d.byteArrayDataData(value)
    d.putValue(d.readRawMemory(data, size), Hex2EncodedLatin1)
    d.putNumChild(size)
    if d.isExpanded():
        d.putArrayData(d.charType(), data, size)

171
172
173
def qform__QByteArray():
    return "Inline,As Latin1 in Separate Window,As UTF-8 in Separate Window"

hjk's avatar
hjk committed
174
175
def qdump__QByteArray(d, value):
    d.putByteArrayValue(value)
hjk's avatar
hjk committed
176
    data, size, alloc = d.byteArrayData(value)
177
    d.putNumChild(size)
178
179
180
181
    format = d.currentItemFormat()
    if format == 1:
        d.putDisplay(StopDisplay)
    elif format == 2:
182
        d.putField("editformat", DisplayLatin1String)
hjk's avatar
hjk committed
183
        d.putField("editvalue", d.encodeByteArray(value, None))
184
    elif format == 3:
185
        d.putField("editformat", DisplayUtf8String)
hjk's avatar
hjk committed
186
        d.putField("editvalue", d.encodeByteArray(value, None))
hjk's avatar
hjk committed
187
    if d.isExpanded():
188
        d.putArrayData(d.charType(), data, size)
hjk's avatar
hjk committed
189
190


191
192
193
194
195
196
197
198
199
200
201
202
203
# Fails on Windows.
try:
    import curses.ascii
    def printableChar(ucs):
        if curses.ascii.isprint(ucs):
            return ucs
        return '?'
except:
    def printableChar(ucs):
        if ucs >= 32 and ucs <= 126:
            return ucs
        return '?'

hjk's avatar
hjk committed
204
205
def qdump__QChar(d, value):
    ucs = int(value["ucs"])
206
    d.putValue("'%c' (%d)" % (printableChar(ucs), ucs))
hjk's avatar
hjk committed
207
208
209
    d.putNumChild(0)


hjk's avatar
hjk committed
210
def qform__QAbstractItemModel():
211
    return "Normal,Enhanced"
212

hjk's avatar
hjk committed
213
def qdump__QAbstractItemModel(d, value):
214
215
216
217
218
    format = d.currentItemFormat()
    if format == 1:
        d.putPlainChildren(value)
        return
    #format == 2:
219
    # Create a default-constructed QModelIndex on the stack.
220
    try:
221
        ri = makeValue(d.ns + "QModelIndex", "-1, -1, 0, 0")
hjk's avatar
hjk committed
222
        this_ = makeExpression(value)
223
        ri_ = makeExpression(ri)
224
225
226
        rowCount = int(parseAndEvaluate("%s.rowCount(%s)" % (this_, ri_)))
        columnCount = int(parseAndEvaluate("%s.columnCount(%s)" % (this_, ri_)))
    except:
hjk's avatar
hjk committed
227
        d.putPlainChildren(value)
228
        return
229
230
    d.putValue("%d x %d" % (rowCount, columnCount))
    d.putNumChild(rowCount * columnCount)
hjk's avatar
hjk committed
231
232
    if d.isExpanded():
        with Children(d, numChild=rowCount * columnCount, childType=ri.type):
233
234
235
            i = 0
            for row in xrange(rowCount):
                for column in xrange(columnCount):
hjk's avatar
hjk committed
236
                    with SubItem(d, i):
237
238
239
240
241
242
                        d.putName("[%s, %s]" % (row, column))
                        mi = parseAndEvaluate("%s.index(%d,%d,%s)"
                            % (this_, row, column, ri_))
                        #warn("MI: %s " % mi)
                        #name = "[%d,%d]" % (row, column)
                        #d.putValue("%s" % mi)
hjk's avatar
hjk committed
243
                        d.putItem(mi)
244
245
246
247
248
249
250
251
                        i = i + 1
                        #warn("MI: %s " % mi)
                        #d.putName("[%d,%d]" % (row, column))
                        #d.putValue("%s" % mi)
                        #d.putNumChild(0)
                        #d.putType(mi.type)
    #gdb.execute("call free($ri)")

252
253
254
def qform__QModelIndex():
    return "Normal,Enhanced"

hjk's avatar
hjk committed
255
def qdump__QModelIndex(d, value):
256
257
258
259
    format = d.currentItemFormat()
    if format == 1:
        d.putPlainChildren(value)
        return
hjk's avatar
hjk committed
260
261
    r = value["r"]
    c = value["c"]
262
263
264
265
    try:
        p = value["p"]
    except:
        p = value["i"]
hjk's avatar
hjk committed
266
    m = value["m"]
267
268
269
270
271
    if isNull(m) or r < 0 or c < 0:
        d.putValue("(invalid)")
        d.putPlainChildren(value)
        return

272
273
    mm = m.dereference()
    mm = mm.cast(mm.type.unqualified())
274
    try:
275
276
277
        mi = makeValue(d.ns + "QModelIndex", "%s,%s,%s,%s" % (r, c, p, m))
        mm_ = makeExpression(mm)
        mi_ = makeExpression(mi)
278
279
280
        rowCount = int(parseAndEvaluate("%s.rowCount(%s)" % (mm_, mi_)))
        columnCount = int(parseAndEvaluate("%s.columnCount(%s)" % (mm_, mi_)))
    except:
281
        d.putEmptyValue()
hjk's avatar
hjk committed
282
        d.putPlainChildren(value)
283
        return
284
285
286

    try:
        # Access DisplayRole as value
hjk's avatar
hjk committed
287
288
        val = parseAndEvaluate("%s.data(%s, 0)" % (mm_, mi_))
        v = val["d"]["data"]["ptr"]
289
290
291
292
        d.putStringValue(makeValue(d.ns + 'QString', v))
    except:
        d.putValue("(invalid)")

293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
    d.putNumChild(rowCount * columnCount)
    if d.isExpanded():
        with Children(d):
            i = 0
            for row in xrange(rowCount):
                for column in xrange(columnCount):
                    with UnnamedSubItem(d, i):
                        d.putName("[%s, %s]" % (row, column))
                        mi2 = parseAndEvaluate("%s.index(%d,%d,%s)"
                            % (mm_, row, column, mi_))
                        d.putItem(mi2)
                        i = i + 1
            #d.putCallItem("parent", val, "parent")
            #with SubItem(d, "model"):
            #    d.putValue(m)
            #    d.putType(d.ns + "QAbstractItemModel*")
            #    d.putNumChild(1)
310
311
    #gdb.execute("call free($mi)")

hjk's avatar
hjk committed
312

hjk's avatar
hjk committed
313
def qdump__QDate(d, value):
314
    jd = int(value["jd"])
315
316
317
318
319
    if int(jd):
        d.putValue(jd, JulianDate)
        d.putNumChild(1)
        if d.isExpanded():
            qt = d.ns + "Qt::"
320
321
            if lldbLoaded:
                qt += "DateFormat::" # FIXME: Bug?...
322
323
324
325
326
327
328
329
330
331
            # FIXME: This improperly uses complex return values.
            with Children(d):
                d.putCallItem("toString", value, "toString", qt + "TextDate")
                d.putCallItem("(ISO)", value, "toString", qt + "ISODate")
                d.putCallItem("(SystemLocale)", value, "toString",
                    qt + "SystemLocaleDate")
                d.putCallItem("(Locale)", value, "toString", qt + "LocaleDate")
    else:
        d.putValue("(invalid)")
        d.putNumChild(0)
hjk's avatar
hjk committed
332
333


hjk's avatar
hjk committed
334
def qdump__QTime(d, value):
335
336
337
338
339
    mds = value["mds"]
    if int(mds) >= 0:
        d.putValue(value["mds"], MillisecondsSinceMidnight)
        d.putNumChild(1)
        if d.isExpanded():
340
341
            qtdate = d.ns + "Qt::"
            qttime = d.ns + "Qt::"
342
            if lldbLoaded:
343
344
                qtdate += "DateFormat::" # FIXME: Bug?...
                qttime += "TimeSpec::"
345
346
            # FIXME: This improperly uses complex return values.
            with Children(d):
347
348
                d.putCallItem("toString", value, "toString", qtdate + "TextDate")
                d.putCallItem("(ISO)", value, "toString", qtdate + "ISODate")
349
                d.putCallItem("(SystemLocale)", value, "toString",
350
351
352
                     qtdate + "SystemLocaleDate")
                d.putCallItem("(Locale)", value, "toString", qtdate + "LocaleDate")
                d.putCallItem("toUTC", value, "toTimeSpec", qttime + "UTC")
353
354
355
    else:
        d.putValue("(invalid)")
        d.putNumChild(0)
356
357


358
359
# This relies on the Qt4/Qt5 internal structure layout:
# {sharedref(4), date(8), time(4+x)}
hjk's avatar
hjk committed
360
def qdump__QDateTime(d, value):
361
362
363
364
    intType = d.lookupType("int")
    intPtrType = intType.pointer()
    base = pointerValue(value.cast(intPtrType))
    mds = int(createReferenceValue(value, base + intPtrType.sizeof + 8, intType))
365
    if mds >= 0:
366
367
        jd = int(createReferenceValue(value, base + intPtrType.sizeof, intType))
        d.putValue("%s/%s" % (jd, mds), JulianDateAndMillisecondsSinceMidnight)
368
369
370
371
        d.putNumChild(1)
        if d.isExpanded():
            # FIXME: This improperly uses complex return values.
            with Children(d):
372
373
                qtdate = d.ns + "Qt::"
                qttime = d.ns + "Qt::"
374
                if lldbLoaded:
375
376
                    qtdate += "DateFormat::" # FIXME: Bug?...
                    qttime += "TimeSpec::" # FIXME: Bug?...
377
                d.putCallItem("toTime_t", value, "toTime_t")
378
379
380
381
382
383
                d.putCallItem("toString", value, "toString", qtdate + "TextDate")
                d.putCallItem("(ISO)", value, "toString", qtdate + "ISODate")
                d.putCallItem("(SystemLocale)", value, "toString", qtdate + "SystemLocaleDate")
                d.putCallItem("(Locale)", value, "toString", qtdate + "LocaleDate")
                d.putCallItem("toUTC", value, "toTimeSpec", qttime + "UTC")
                d.putCallItem("toLocalTime", value, "toTimeSpec", qttime + "LocalTime")
384
385
386
    else:
        d.putValue("(invalid)")
        d.putNumChild(0)
hjk's avatar
hjk committed
387
388
389


def qdump__QDir(d, value):
hjk's avatar
hjk committed
390
    d.putNumChild(1)
391
392
393
394
395
396
397
    data = value["d_ptr"]["d"].dereference()
    try:
        # Up to Qt 4.7
        d.putStringValue(data["path"])
    except:
        # Qt 4.8 and later.
        d.putStringValue(data["dirEntry"]["m_filePath"])
hjk's avatar
hjk committed
398
399
    if d.isExpanded():
        with Children(d):
400
            qdir = d.ns + "QDir::"
hjk's avatar
hjk committed
401
402
            d.putCallItem("absolutePath", value, "absolutePath")
            d.putCallItem("canonicalPath", value, "canonicalPath")
hjk's avatar
hjk committed
403
404
            d.putSubItem("entryList", data["files"])
            d.putSubItem("entryInfoList", data["fileInfos"])
hjk's avatar
hjk committed
405
406


hjk's avatar
hjk committed
407
def qdump__QFile(d, value):
hjk's avatar
hjk committed
408
    try:
409
        ptype = d.lookupType(d.ns + "QFilePrivate").pointer()
hjk's avatar
hjk committed
410
411
412
413
414
        d_ptr = value["d_ptr"]["d"]
        d.putStringValue(d_ptr.cast(ptype).dereference()["fileName"])
    except:
        d.putPlainChildren(value)
        return
415
    d.putNumChild(1)
hjk's avatar
hjk committed
416
417
    if d.isExpanded():
        with Children(d):
hjk's avatar
hjk committed
418
            base = fieldAt(value.type, 0).type
419
            d.putSubItem("[%s]" % str(base), value.cast(base), False)
hjk's avatar
hjk committed
420
            d.putCallItem("exists", value, "exists")
hjk's avatar
hjk committed
421
422


hjk's avatar
hjk committed
423
def qdump__QFileInfo(d, value):
424
    try:
hjk's avatar
hjk committed
425
        d.putStringValue(value["d_ptr"]["d"].dereference()["fileNames"][3])
426
    except:
hjk's avatar
hjk committed
427
        d.putPlainChildren(value)
428
        return
hjk's avatar
hjk committed
429
    d.putNumChild(1)
hjk's avatar
hjk committed
430
    if d.isExpanded():
431
        with Children(d, childType=d.lookupType(d.ns + "QString")):
hjk's avatar
hjk committed
432
433
434
435
436
437
438
            d.putCallItem("absolutePath", value, "absolutePath")
            d.putCallItem("absoluteFilePath", value, "absoluteFilePath")
            d.putCallItem("canonicalPath", value, "canonicalPath")
            d.putCallItem("canonicalFilePath", value, "canonicalFilePath")
            d.putCallItem("completeBaseName", value, "completeBaseName")
            d.putCallItem("completeSuffix", value, "completeSuffix")
            d.putCallItem("baseName", value, "baseName")
439
440
            if False:
                #ifdef Q_OS_MACX
hjk's avatar
hjk committed
441
442
443
444
                d.putCallItem("isBundle", value, "isBundle")
                d.putCallItem("bundleName", value, "bundleName")
            d.putCallItem("fileName", value, "fileName")
            d.putCallItem("filePath", value, "filePath")
445
            # Crashes gdb (archer-tromey-python, at dad6b53fe)
hjk's avatar
hjk committed
446
447
448
            #d.putCallItem("group", value, "group")
            #d.putCallItem("owner", value, "owner")
            d.putCallItem("path", value, "path")
449

hjk's avatar
hjk committed
450
451
            d.putCallItem("groupid", value, "groupId")
            d.putCallItem("ownerid", value, "ownerId")
452
453

            #QFile::Permissions permissions () const
hjk's avatar
hjk committed
454
            perms = call(value, "permissions")
455
456
457
            if perms is None:
                d.putValue("<not available>")
            else:
hjk's avatar
hjk committed
458
                with SubItem(d, "permissions"):
459
                    d.putEmptyValue()
460
461
                    d.putType(d.ns + "QFile::Permissions")
                    d.putNumChild(10)
hjk's avatar
hjk committed
462
                    if d.isExpanded():
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
                        with Children(d, 10):
                            perms = perms['i']
                            d.putBoolItem("ReadOwner",  perms & 0x4000)
                            d.putBoolItem("WriteOwner", perms & 0x2000)
                            d.putBoolItem("ExeOwner",   perms & 0x1000)
                            d.putBoolItem("ReadUser",   perms & 0x0400)
                            d.putBoolItem("WriteUser",  perms & 0x0200)
                            d.putBoolItem("ExeUser",    perms & 0x0100)
                            d.putBoolItem("ReadGroup",  perms & 0x0040)
                            d.putBoolItem("WriteGroup", perms & 0x0020)
                            d.putBoolItem("ExeGroup",   perms & 0x0010)
                            d.putBoolItem("ReadOther",  perms & 0x0004)
                            d.putBoolItem("WriteOther", perms & 0x0002)
                            d.putBoolItem("ExeOther",   perms & 0x0001)

            #QDir absoluteDir () const
            #QDir dir () const
hjk's avatar
hjk committed
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
            d.putCallItem("caching", value, "caching")
            d.putCallItem("exists", value, "exists")
            d.putCallItem("isAbsolute", value, "isAbsolute")
            d.putCallItem("isDir", value, "isDir")
            d.putCallItem("isExecutable", value, "isExecutable")
            d.putCallItem("isFile", value, "isFile")
            d.putCallItem("isHidden", value, "isHidden")
            d.putCallItem("isReadable", value, "isReadable")
            d.putCallItem("isRelative", value, "isRelative")
            d.putCallItem("isRoot", value, "isRoot")
            d.putCallItem("isSymLink", value, "isSymLink")
            d.putCallItem("isWritable", value, "isWritable")
            d.putCallItem("created", value, "created")
            d.putCallItem("lastModified", value, "lastModified")
            d.putCallItem("lastRead", value, "lastRead")


def qdump__QFixed(d, value):
    v = int(value["val"])
hjk's avatar
hjk committed
499
500
501
502
    d.putValue("%s/64 = %s" % (v, v/64.0))
    d.putNumChild(0)


hjk's avatar
hjk committed
503
def qdump__QFiniteStack(d, value):
hjk's avatar
hjk committed
504
505
    alloc = int(value["_alloc"])
    size = int(value["_size"])
hjk's avatar
hjk committed
506
507
508
509
    check(0 <= size and size <= alloc and alloc <= 1000 * 1000 * 1000)
    d.putItemCount(size)
    d.putNumChild(size)
    if d.isExpanded():
510
        innerType = d.templateArgument(value.type, 0)
hjk's avatar
hjk committed
511
512
        d.putArrayData(innerType, value["_array"], size)

513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
# Stock gdb 7.2 seems to have a problem with types here:
#
#  echo -e "namespace N { struct S { enum E { zero, one, two }; }; }\n"\
#      "int main() { N::S::E x = N::S::one;\n return x; }" >> main.cpp
#  g++ -g main.cpp
#  gdb-7.2 -ex 'file a.out' -ex 'b main' -ex 'run' -ex 'step' \
#     -ex 'ptype N::S::E' -ex 'python print gdb.lookup_type("N::S::E")' -ex 'q'
#  gdb-7.1 -ex 'file a.out' -ex 'b main' -ex 'run' -ex 'step' \
#     -ex 'ptype N::S::E' -ex 'python print gdb.lookup_type("N::S::E")' -ex 'q'
#  gdb-cvs -ex 'file a.out' -ex 'b main' -ex 'run' -ex 'step' \
#     -ex 'ptype N::S::E' -ex 'python print gdb.lookup_type("N::S::E")' -ex 'q'
#
# gives as of 2010-11-02
#
#  type = enum N::S::E {N::S::zero, N::S::one, N::S::two} \n
hjk's avatar
hjk committed
528
529
#    Traceback (most recent call last): File "<string>", line 1,
#      in <module> RuntimeError: No type named N::S::E.
530
531
532
533
534
#  type = enum N::S::E {N::S::zero, N::S::one, N::S::two} \n  N::S::E
#  type = enum N::S::E {N::S::zero, N::S::one, N::S::two} \n  N::S::E
#
# i.e. there's something broken in stock 7.2 that is was ok in 7.1 and is ok later.

hjk's avatar
hjk committed
535
536
def qdump__QFlags(d, value):
    i = value["i"]
537
    try:
538
        enumType = d.templateArgument(value.type.unqualified(), 0)
539
540
541
        d.putValue("%s (%s)" % (i.cast(enumType), i))
    except:
        d.putValue("%s" % i)
hjk's avatar
hjk committed
542
543
544
    d.putNumChild(0)


545
546
547
def qform__QHash():
    return mapForms()

hjk's avatar
hjk committed
548
def qdump__QHash(d, value):
hjk's avatar
hjk committed
549
550

    def hashDataFirstNode(value):
hjk's avatar
hjk committed
551
552
553
        val = value.cast(hashDataType)
        bucket = val["buckets"]
        e = val.cast(hashNodeType)
hjk's avatar
hjk committed
554
        for n in xrange(int(val["numBuckets"]) - 1, -1, -1):
hjk's avatar
hjk committed
555
556
557
558
559
560
561
562
563
564
565
566
567
            n = n - 1
            if n < 0:
                break
            if bucket.dereference() != e:
                return bucket.dereference()
            bucket = bucket + 1
        return e;

    def hashDataNextNode(node):
        next = node["next"]
        if next["next"]:
            return next
        d = node.cast(hashDataType.pointer()).dereference()
hjk's avatar
hjk committed
568
569
        numBuckets = int(d["numBuckets"])
        start = (int(node["h"]) % numBuckets) + 1
hjk's avatar
hjk committed
570
        bucket = d["buckets"] + start
571
        for n in xrange(numBuckets - start):
hjk's avatar
hjk committed
572
573
574
575
576
            if bucket.dereference() != next:
                return bucket.dereference()
            bucket += 1
        return node

577
578
    keyType = d.templateArgument(value.type, 0)
    valueType = d.templateArgument(value.type, 1)
hjk's avatar
hjk committed
579

hjk's avatar
hjk committed
580
581
    d_ptr = value["d"]
    e_ptr = value["e"]
hjk's avatar
hjk committed
582
    size = int(d_ptr["size"])
hjk's avatar
hjk committed
583
584
585
586

    hashDataType = d_ptr.type
    hashNodeType = e_ptr.type

587
    check(0 <= size and size <= 100 * 1000 * 1000)
588
    checkRef(d_ptr["ref"])
hjk's avatar
hjk committed
589

590
591
    d.putItemCount(size)
    d.putNumChild(size)
hjk's avatar
hjk committed
592
    if d.isExpanded():
hjk's avatar
hjk committed
593
        isCompact = d.isMapCompact(keyType, valueType)
hjk's avatar
hjk committed
594
        node = hashDataFirstNode(value)
hjk's avatar
hjk committed
595
        innerType = e_ptr.dereference().type
596
        childType = innerType
597
        if isCompact:
598
            childType = valueType
hjk's avatar
hjk committed
599
        with Children(d, size, maxNumChild=1000, childType=childType):
600
601
            for i in d.childRange():
                it = node.dereference().cast(innerType)
hjk's avatar
hjk committed
602
                with SubItem(d, i):
603
604
605
                    if isCompact:
                        d.putMapName(it["key"])
                        d.putItem(it["value"])
606
607
                        d.putType(valueType)
                    else:
hjk's avatar
hjk committed
608
                        d.putItem(it)
609
                node = hashDataNextNode(node)
hjk's avatar
hjk committed
610
611


hjk's avatar
hjk committed
612
def qdump__QHashNode(d, value):
613
614
    keyType = d.templateArgument(value.type, 0)
    valueType = d.templateArgument(value.type, 1)
hjk's avatar
hjk committed
615
616
    key = value["key"]
    val = value["value"]
hjk's avatar
hjk committed
617

618
619
620
621
    #if isSimpleType(keyType) and isSimpleType(valueType):
    #    d.putName(key)
    #    d.putValue(val)
    #else:
622
    d.putEmptyValue()
hjk's avatar
hjk committed
623

624
    d.putNumChild(2)
hjk's avatar
hjk committed
625
    if d.isExpanded():
626
        with Children(d):
hjk's avatar
hjk committed
627
628
            d.putSubItem("key", key)
            d.putSubItem("value", val)
hjk's avatar
hjk committed
629
630


631
632
def qHashIteratorHelper(d, value):
    typeName = str(value.type)
633
    hashType = d.lookupType(typeName[0:typeName.rfind("::")])
634
635
    keyType = d.templateArgument(hashType, 0)
    valueType = d.templateArgument(hashType, 1)
636
    d.putNumChild(1)
637
    d.putEmptyValue()
638
639
640
    if d.isExpanded():
        with Children(d):
            typeName = "%sQHash<%s,%s>::Node" % (d.ns, keyType, valueType)
641
            node = value["i"].cast(d.lookupType(typeName).pointer())
642
643
644
645
646
647
648
649
650
651
            d.putSubItem("key", node["key"])
            d.putSubItem("value", node["value"])

def qdump__QHash__const_iterator(d, value):
    qHashIteratorHelper(d, value)

def qdump__QHash__iterator(d, value):
    qHashIteratorHelper(d, value)


hjk's avatar
hjk committed
652
653
def qdump__QHostAddress(d, value):
    data = value["d"]["d"].dereference()
hjk's avatar
hjk committed
654
655
656
    if int(data["ipString"]["d"]["size"]):
        d.putStringValue(data["ipString"])
    else:
657
        a = int(data["a"])
658
659
660
661
662
        a, n4 = divmod(a, 256)
        a, n3 = divmod(a, 256)
        a, n2 = divmod(a, 256)
        a, n1 = divmod(a, 256)
        d.putValue("%d.%d.%d.%d" % (n1, n2, n3, n4));
hjk's avatar
hjk committed
663
    d.putNumChild(1)
hjk's avatar
hjk committed
664
    if d.isExpanded():
hjk's avatar
hjk committed
665
        with Children(d):
hjk's avatar
hjk committed
666
           d.putFields(data)
hjk's avatar
hjk committed
667

hjk's avatar
hjk committed
668
def qdump__QList(d, value):
669
670
671
672
673
    dptr = childAt(value, 0)["d"]
    private = dptr.dereference()
    begin = int(private["begin"])
    end = int(private["end"])
    array = private["array"]
hjk's avatar
hjk committed
674
    check(begin >= 0 and end >= 0 and end <= 1000 * 1000 * 1000)
675
676
    size = end - begin
    check(size >= 0)
677
    checkRef(private["ref"])
hjk's avatar
hjk committed
678

679
    innerType = d.templateArgument(value.type, 0)
hjk's avatar
hjk committed
680

681
682
    d.putItemCount(size)
    d.putNumChild(size)
hjk's avatar
hjk committed
683
    if d.isExpanded():
hjk's avatar
hjk committed
684
685
686
687
688
689
        innerSize = innerType.sizeof
        # The exact condition here is:
        #  QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic
        # but this data is available neither in the compiled binary nor
        # in the frontend.
        # So as first approximation only do the 'isLarge' check:
690
691
692
693
694
695
696
697
698
699
700
701
        stepSize = dptr.type.sizeof
        isInternal = innerSize <= stepSize and d.isMovableType(innerType)
        addr = d.addressOf(array) + begin * stepSize
        if isInternal:
            if innerSize == stepSize:
                p = createPointerValue(value, addr, innerType)
                d.putArrayData(innerType, p, size)
            else:
                with Children(d, size, childType=innerType):
                    for i in d.childRange():
                        p = createPointerValue(value, addr + i * stepSize, innerType)
                        d.putSubItem(i, p.dereference())
702
        else:
703
704
705
706
707
708
            p = createPointerValue(value, addr, innerType.pointer())
            # about 0.5s / 1000 items
            with Children(d, size, maxNumChild=2000, childType=innerType):
                for i in d.childRange():
                    d.putSubItem(i, p.dereference().dereference())
                    p += 1
hjk's avatar
hjk committed
709

hjk's avatar
hjk committed
710
711
def qform__QImage():
    return "Normal,Displayed"
hjk's avatar
hjk committed
712

hjk's avatar
hjk committed
713
def qdump__QImage(d, value):
714
715
    # This relies on current QImage layout
    intPtrType = d.lookupType("int").pointer()
716
717
    offset = (3 if d.qtVersion() >= 0x050000 else 2) * intPtrType.sizeof
    base = createReferenceValue(value, d.addressOf(value) + offset, intPtrType)
718
719
720
    width = int(base[1])
    height = int(base[2])
    d.putValue("(%dx%d)" % (width, height))
721
    d.putNumChild(0)
hjk's avatar
hjk committed
722
    #d.putNumChild(1)
hjk's avatar
hjk committed
723
    if d.isExpanded():
724
        with Children(d):
hjk's avatar
hjk committed
725
            with SubItem(d, "data"):
726
                d.putNoType()
727
728
                d.putNumChild(0)
                d.putValue("size: %s bytes" % nbytes);
hjk's avatar
hjk committed
729
    format = d.currentItemFormat()
730
    if format == 1:
731
        d.putDisplay(StopDisplay)
732
    elif format == 2:
733
734
735
        d_ptr = value["d"]
        bits = d_ptr["data"]
        nbytes = d_ptr["nbytes"]
hjk's avatar
hjk committed
736
737
738
        if False:
            # Take four bytes at a time, this is critical for performance.
            # In fact, even four at a time is too slow beyond 100x100 or so.
739
            d.putField("editformat", DisplayImageData)
740
            d.put('%s="' % name)
741
742
            d.put("%08x" % width)
            d.put("%08x" % height)
hjk's avatar
hjk committed
743
            d.put("%08x" % int(d_ptr["format"]))
744
            p = bits.cast(d.intType().pointer())
hjk's avatar
hjk committed
745
746
747
            for i in xrange(nbytes / 4):
                d.put("%08x" % int(p.dereference()))
                p += 1
748
            d.put('",')
hjk's avatar
hjk committed
749
750
751
        else:
            # Write to an external file. Much faster ;-(
            file = tempfile.mkstemp(prefix="gdbpy_")
752
            filename = file[1].replace("\\", "\\\\")
753
            p = bits.cast(d.charType().pointer())
hjk's avatar
hjk committed
754
755
            gdb.execute("dump binary memory %s %s %s" %
                (filename, cleanAddress(p), cleanAddress(p + nbytes)))
756
            d.putDisplay(DisplayImageFile, " %d %d %d %s"
757
                % (width, height, d_ptr["format"], filename))
hjk's avatar
hjk committed
758
759


hjk's avatar
hjk committed
760
761
762
def qdump__QLinkedList(d, value):
    d_ptr = value["d"]
    e_ptr = value["e"]
hjk's avatar
hjk committed
763
    n = int(d_ptr["size"])
hjk's avatar
hjk committed
764
    check(0 <= n and n <= 100*1000*1000)
765
    checkRef(d_ptr["ref"])
hjk's avatar
hjk committed
766
    d.putItemCount(n)
767
    d.putNumChild(n)
hjk's avatar
hjk committed
768
    if d.isExpanded():
769
        innerType = d.templateArgument(value.type, 0)
hjk's avatar
hjk committed
770
        with Children(d, n, maxNumChild=1000, childType=innerType):
771
772
            p = e_ptr["n"]
            for i in d.childRange():
hjk's avatar
hjk committed
773
                d.putSubItem(i, p["t"])
774
                p = p["n"]
hjk's avatar
hjk committed
775

776
qqLocalesCount = None
hjk's avatar
hjk committed
777

hjk's avatar
hjk committed
778
def qdump__QLocale(d, value):
779
780
781
782
    # Check for uninitialized 'index' variable. Retrieve size of
    # QLocale data array from variable in qlocale.cpp.
    # Default is 368 in Qt 4.8, 438 in Qt 5.0.1, the last one
    # being 'System'.
hjk's avatar
hjk committed
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
    #global qqLocalesCount
    #if qqLocalesCount is None:
    #    #try:
    #        qqLocalesCount = int(value(d.ns + 'locale_data_size'))
    #    #except:
    #        qqLocalesCount = 438
    #try:
    #    index = int(value["p"]["index"])
    #except:
    #    try:
    #        index = int(value["d"]["d"]["m_index"])
    #    except:
    #        index = int(value["d"]["d"]["m_data"]...)
    #check(index >= 0)
    #check(index <= qqLocalesCount)
hjk's avatar
hjk committed
798
    d.putStringValue(call(value, "name"))
799
800
801
    d.putNumChild(0)
    return
    # FIXME: Poke back for variants.
hjk's avatar
hjk committed
802
    if d.isExpanded():
803
        with Children(d, childType=d.lookupType(d.ns + "QChar"), childNumChild=0):
hjk's avatar
hjk committed
804
805
806
807
808
            d.putCallItem("country", value, "country")
            d.putCallItem("language", value, "language")
            d.putCallItem("measurementSystem", value, "measurementSystem")
            d.putCallItem("numberOptions", value, "numberOptions")
            d.putCallItem("timeFormat_(short)", value,
hjk's avatar
hjk committed
809
                "timeFormat", d.ns + "QLocale::ShortFormat")
hjk's avatar
hjk committed
810
            d.putCallItem("timeFormat_(long)", value,
hjk's avatar
hjk committed
811
                "timeFormat", d.ns + "QLocale::LongFormat")
hjk's avatar
hjk committed
812
813
814
815
816
817
            d.putCallItem("decimalPoint", value, "decimalPoint")
            d.putCallItem("exponential", value, "exponential")
            d.putCallItem("percent", value, "percent")
            d.putCallItem("zeroDigit", value, "zeroDigit")
            d.putCallItem("groupSeparator", value, "groupSeparator")
            d.putCallItem("negativeSign", value, "negativeSign")
hjk's avatar
hjk committed
818
819


hjk's avatar
hjk committed
820
def qdump__QMapNode(d, value):
821
    d.putEmptyValue()
822
    d.putNumChild(2)
hjk's avatar
hjk committed
823
824
825
826
827
828
    if d.isExpanded():
        with Children(d):
            d.putSubItem("key", value["key"])
            d.putSubItem("value", value["value"])


829
def qdumpHelper__Qt4_QMap(d, value, forceLong):
hjk's avatar
hjk committed
830
831
    d_ptr = value["d"].dereference()
    e_ptr = value["e"].dereference()
hjk's avatar
hjk committed
832
    n = int(d_ptr["size"])
hjk's avatar
hjk committed
833
    check(0 <= n and n <= 100*1000*1000)
834
    checkRef(d_ptr["ref"])
hjk's avatar
hjk committed
835
836

    d.putItemCount(n)
837
    d.putNumChild(n)
hjk's avatar
hjk committed
838
    if d.isExpanded():
839
840
        if n > 10000:
            n = 10000
hjk's avatar
hjk committed
841

842
843
        keyType = d.templateArgument(value.type, 0)
        valueType = d.templateArgument(value.type, 1)
hjk's avatar
hjk committed
844
        isCompact = d.isMapCompact(keyType, valueType)
hjk's avatar
hjk committed
845
846
847
848
849
850

        it = e_ptr["forward"].dereference()

        # QMapPayloadNode is QMapNode except for the 'forward' member, so
        # its size is most likely the offset of the 'forward' member therein.
        # Or possibly 2 * sizeof(void *)
851
        nodeType = d.lookupType(d.ns + "QMapNode<%s, %s>" % (keyType, valueType))
hjk's avatar
hjk committed
852
853
        nodePointerType = nodeType.pointer()
        payloadSize = nodeType.sizeof - 2 * nodePointerType.sizeof
hjk's avatar
hjk committed
854

855
        if isCompact:
856
857
858
            innerType = valueType
        else:
            innerType = nodeType
hjk's avatar
hjk committed
859

hjk's avatar
hjk committed
860
        with Children(d, n, childType=innerType):
861
            for i in xrange(n):
hjk's avatar
hjk committed
862
863
                base = it.cast(d.charPtrType()) - payloadSize
                node = base.cast(nodePointerType).dereference()
hjk's avatar
hjk committed
864
                with SubItem(d, i):
865
                    d.putField("iname", d.currentIName)
866
                    if isCompact:
867
                        #d.putType(valueType)
hjk's avatar
hjk committed
868
                        if forceLong:
869
                            d.putName("[%s] %s" % (i, node["key"]))
hjk's avatar
hjk committed
870
                        else:
871
872
                            d.putMapName(node["key"])
                        d.putItem(node["value"])
873
                    else:
hjk's avatar
hjk committed
874
                        d.putItem(node)
875
                it = it.dereference()["forward"].dereference()
hjk's avatar
hjk committed
876
877


878
879
def qdumpHelper__Qt5_QMap(d, value, forceLong):
    d_ptr = value["d"].dereference()
hjk's avatar
hjk committed
880
    n = int(d_ptr["size"])
881
882
883
884
885
886
887
888
889
    check(0 <= n and n <= 100*1000*1000)
    checkRef(d_ptr["ref"])

    d.putItemCount(n)
    d.putNumChild(n)
    if d.isExpanded():
        if n > 10000:
            n = 10000

890
891
        keyType = d.templateArgument(value.type, 0)
        valueType = d.templateArgument(value.type, 1)
hjk's avatar
hjk committed
892
        isCompact = d.isMapCompact(keyType, valueType)
893
        nodeType = d.lookupType(d.ns + "QMapNode<%s, %s>" % (keyType, valueType))
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
        if isCompact:
            innerType = valueType
        else:
            innerType = nodeType

        with Children(d, n, childType=innerType):
            toDo = []
            i = -1
            node = d_ptr["header"]
            left = node["left"]
            if not isNull(left):
                toDo.append(left.dereference())
            right = node["right"]
            if not isNull(right):
                toDo.append(right.dereference())

            while len(toDo):
                node = toDo[0].cast(nodeType)
                toDo = toDo[1:]
                left = node["left"]
                if not isNull(left):
                    toDo.append(left.dereference())
                right = node["right"]
                if not isNull(right):
                    toDo.append(right.dereference())
                i += 1

                with SubItem(d, i):
922
                    d.putField("iname", d.currentIName)
923
924
925
926
927
928
929
930
931
932
933
                    if isCompact:
                        if forceLong:
                            d.putName("[%s] %s" % (i, node["key"]))
                        else:
                            d.putMapName(node["key"])
                        d.putItem(node["value"])
                    else:
                        qdump__QMapNode(d, node)


def qdumpHelper__QMap(d, value, forceLong):
hjk's avatar
hjk committed
934
    if fieldAt(value["d"].dereference().type, 0).name == "backward":
935
936
937
938
        qdumpHelper__Qt4_QMap(d, value, forceLong)
    else:
        qdumpHelper__Qt5_QMap(d, value, forceLong)

939
940
941
def qform__QMap():
    return mapForms()

hjk's avatar
hjk committed
942
943
def qdump__QMap(d, value):
    qdumpHelper__QMap(d, value, False)
hjk's avatar
hjk committed
944

945
946
947
def qform__QMultiMap():
    return mapForms()

hjk's avatar
hjk committed
948
949
def qdump__QMultiMap(d, value):
    qdumpHelper__QMap(d, value, True)
hjk's avatar
hjk committed
950
951
952
953
954
955
956
957
958
959
960
961
962


def extractCString(table, offset):
    result = ""
    while True:
        d = table[offset]
        if d == 0:
            break
        result += "%c" % d
        offset += 1
    return result


hjk's avatar
hjk committed
963
964
def qdump__QObject(d, value):
    #warn("OBJECT: %s " % value)
hjk's avatar
hjk committed
965
966
    d.tryPutObjectNameValue(value)

967
    try:
hjk's avatar
hjk committed
968
        privateTypeName = d.ns + "QObjectPrivate"
969
        privateType = d.lookupType(privateTypeName)
hjk's avatar
hjk committed
970
        staticMetaObject = value["staticMetaObject"]
971
    except:
hjk's avatar