~ubuntu-branches/debian/sid/kdevelop/sid

« back to all changes in this revision

Viewing changes to debuggers/gdb/printers/qt4.py

  • Committer: Bazaar Package Importer
  • Author(s): Jeremy Lainé
  • Date: 2010-05-05 07:21:55 UTC
  • mfrom: (1.2.3 upstream) (5.1.2 squeeze)
  • Revision ID: james.westby@ubuntu.com-20100505072155-h78lx19pu04sbhtn
Tags: 4:4.0.0-2
* Upload to unstable (Closes: #579947, #481832).
* Acknowledge obsolete NMU fixes (Closes: #562410, #546961).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: iso-8859-1 -*-
 
2
# Pretty-printers for Qt4.
 
3
 
 
4
# Copyright (C) 2009 Niko Sams <niko.sams@gmail.com>
 
5
 
 
6
# This program is free software; you can redistribute it and/or modify
 
7
# it under the terms of the GNU General Public License as published by
 
8
# the Free Software Foundation; either version 2 of the License, or
 
9
# (at your option) any later version.
 
10
#
 
11
# This program is distributed in the hope that it will be useful,
 
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
# GNU General Public License for more details.
 
15
#
 
16
# You should have received a copy of the GNU General Public License
 
17
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
18
 
 
19
import gdb
 
20
import itertools
 
21
import re
 
22
 
 
23
class QStringPrinter:
 
24
 
 
25
    def __init__(self, val):
 
26
        self.val = val
 
27
 
 
28
    def to_string(self):
 
29
        #ret = ""
 
30
        #i = 0
 
31
        size = self.val['d']['size']
 
32
        #while i < size:
 
33
        #    char = self.val['d']['data'][i]
 
34
        #    if (char > 127):
 
35
        #        ret += "\\u%x" % int(char)
 
36
        #    else:
 
37
        #        ret += chr(char)
 
38
        #    i = i + 1
 
39
        #return ret
 
40
        dataAsCharPointer = self.val['d']['data'].cast(gdb.lookup_type("char").pointer())
 
41
        return dataAsCharPointer.string(encoding = 'UTF-16', length = size * 2)
 
42
 
 
43
    def display_hint (self):
 
44
        return 'string'
 
45
 
 
46
 
 
47
class QByteArrayPrinter:
 
48
 
 
49
    def __init__(self, val):
 
50
        self.val = val
 
51
 
 
52
    class _iterator:
 
53
        def __init__(self, data, size):
 
54
            self.data = data
 
55
            self.size = size
 
56
            self.count = 0
 
57
 
 
58
        def __iter__(self):
 
59
            return self
 
60
 
 
61
        def next(self):
 
62
            if self.count >= self.size:
 
63
                raise StopIteration
 
64
            count = self.count
 
65
            self.count = self.count + 1
 
66
            return ('[%d]' % count, self.data[count])
 
67
 
 
68
    def children(self):
 
69
        return self._iterator(self.val['d']['data'], self.val['d']['size'])
 
70
 
 
71
    def to_string(self):
 
72
        #todo: handle charset correctly
 
73
        return self.val['d']['data'].string()
 
74
 
 
75
    def display_hint (self):
 
76
        return 'string'
 
77
 
 
78
class QListPrinter:
 
79
    "Print a QList"
 
80
 
 
81
    class _iterator:
 
82
        def __init__(self, nodetype, d):
 
83
            self.nodetype = nodetype
 
84
            self.d = d
 
85
            self.count = 0
 
86
 
 
87
        def __iter__(self):
 
88
            return self
 
89
 
 
90
        def next(self):
 
91
            if self.count >= self.d['end'] - self.d['begin']:
 
92
                raise StopIteration
 
93
            count = self.count
 
94
            array = self.d['array'][self.d['begin'] + count]
 
95
 
 
96
            #from QTypeInfo::isLarge
 
97
            isLarge = self.nodetype.sizeof > gdb.lookup_type('void').pointer().sizeof
 
98
 
 
99
            #isStatic is not needed anymore since Qt 4.6
 
100
            #isPointer = self.nodetype.code == gdb.TYPE_CODE_PTR
 
101
            #
 
102
            ##unfortunately we can't use QTypeInfo<T>::isStatic as it's all inlined, so use
 
103
            ##this list of types that use Q_DECLARE_TYPEINFO(T, Q_MOVABLE_TYPE)
 
104
            ##(obviously it won't work for custom types)
 
105
            #movableTypes = ['QRect', 'QRectF', 'QString', 'QMargins', 'QLocale', 'QChar', 'QDate', 'QTime', 'QDateTime', 'QVector',
 
106
            #    'QRegExpr', 'QPoint', 'QPointF', 'QByteArray', 'QSize', 'QSizeF', 'QBitArray', 'QLine', 'QLineF', 'QModelIndex', 'QPersitentModelIndex',
 
107
            #    'QVariant', 'QFileInfo', 'QUrl', 'QXmlStreamAttribute', 'QXmlStreamNamespaceDeclaration', 'QXmlStreamNotationDeclaration',
 
108
            #    'QXmlStreamEntityDeclaration']
 
109
            #if movableTypes.count(self.nodetype.tag):
 
110
            #    isStatic = False
 
111
            #else:
 
112
            #    isStatic = not isPointer
 
113
            isStatic = False
 
114
 
 
115
            if isLarge or isStatic: #see QList::Node::t()
 
116
                node = array.cast(gdb.lookup_type('QList<%s>::Node' % self.nodetype).pointer())
 
117
            else:
 
118
                node = array.cast(gdb.lookup_type('QList<%s>::Node' % self.nodetype))
 
119
            self.count = self.count + 1
 
120
            return ('[%d]' % count, node['v'].cast(self.nodetype))
 
121
 
 
122
    def __init__(self, val, itype):
 
123
        self.val = val
 
124
        if itype == None:
 
125
            self.itype = self.val.type.template_argument(0)
 
126
        else:
 
127
            self.itype = gdb.lookup_type(itype)
 
128
 
 
129
    def children(self):
 
130
        return self._iterator(self.itype, self.val['d'])
 
131
 
 
132
    def to_string(self):
 
133
        if self.val['d']['end'] == self.val['d']['begin']:
 
134
            empty = "empty "
 
135
        else:
 
136
            empty = ""
 
137
 
 
138
        return "%sQList<%s>" % ( empty , self.itype )
 
139
 
 
140
class QMapPrinter:
 
141
    "Print a QMap"
 
142
 
 
143
    class _iterator:
 
144
        def __init__(self, val):
 
145
            self.val = val
 
146
            self.ktype = self.val.type.template_argument(0)
 
147
            self.vtype = self.val.type.template_argument(1)
 
148
            self.data_node = self.val['e']['forward'][0]
 
149
            self.count = 0
 
150
 
 
151
        def __iter__(self):
 
152
            return self
 
153
 
 
154
        def payload (self):
 
155
 
 
156
            #we can't use QMapPayloadNode as it's inlined
 
157
            #as a workaround take the sum of sizeof(members)
 
158
            ret = self.ktype.sizeof
 
159
            ret += self.vtype.sizeof
 
160
            ret += gdb.lookup_type('void').pointer().sizeof
 
161
 
 
162
            #but because of data alignment the value can be higher
 
163
            #so guess it's aliged by sizeof(void*)
 
164
            #TODO: find a real solution for this problem
 
165
            ret += ret % gdb.lookup_type('void').pointer().sizeof
 
166
 
 
167
            ret -= gdb.lookup_type('void').pointer().sizeof
 
168
            return ret
 
169
 
 
170
        def concrete (self, data_node):
 
171
            node_type = gdb.lookup_type('QMapNode<%s, %s>' % (self.ktype, self.vtype)).pointer()
 
172
            return (data_node.cast(gdb.lookup_type('char').pointer()) - self.payload()).cast(node_type)
 
173
 
 
174
        def next(self):
 
175
            if self.data_node == self.val['e']:
 
176
                raise StopIteration
 
177
            node = self.concrete(self.data_node).dereference()
 
178
            if self.count % 2 == 0:
 
179
                item = node['key']
 
180
            else:
 
181
                item = node['value']
 
182
                self.data_node = node['forward'][0]
 
183
 
 
184
            result = ('[%d]' % self.count, item)
 
185
            self.count = self.count + 1
 
186
            return result
 
187
 
 
188
 
 
189
    def __init__(self, val):
 
190
        self.val = val
 
191
 
 
192
    def children(self):
 
193
        return self._iterator(self.val)
 
194
 
 
195
    def to_string(self):
 
196
        if self.val['d']['size'] == 0:
 
197
            empty = "empty "
 
198
        else:
 
199
            empty = ""
 
200
 
 
201
        return "%sQMap<%s, %s>" % ( empty , self.val.type.template_argument(0), self.val.type.template_argument(1) )
 
202
 
 
203
    def display_hint (self):
 
204
        return 'map'
 
205
 
 
206
class QHashPrinter:
 
207
    "Print a QHash"
 
208
 
 
209
    class _iterator:
 
210
        def __init__(self, val):
 
211
            self.val = val
 
212
            self.d = self.val['d']
 
213
            self.ktype = self.val.type.template_argument(0)
 
214
            self.vtype = self.val.type.template_argument(1)
 
215
            self.end_node = self.d.cast(gdb.lookup_type('QHashData::Node').pointer())
 
216
            self.data_node = self.firstNode()
 
217
            self.count = 0
 
218
 
 
219
        def __iter__(self):
 
220
            return self
 
221
 
 
222
        def hashNode (self):
 
223
            "Casts the current QHashData::Node to a QHashNode and returns the result. See also QHash::concrete()"
 
224
            return self.data_node.cast(gdb.lookup_type('QHashNode<%s, %s>' % (self.ktype, self.vtype)).pointer())
 
225
 
 
226
        def firstNode (self):
 
227
            "Get the first node, See QHashData::firstNode()."
 
228
            e = self.d.cast(gdb.lookup_type('QHashData::Node').pointer())
 
229
            #print "QHashData::firstNode() e %s" % e
 
230
            bucketNum = 0
 
231
            bucket = self.d['buckets'][bucketNum]
 
232
            #print "QHashData::firstNode() *bucket %s" % bucket
 
233
            n = self.d['numBuckets']
 
234
            #print "QHashData::firstNode() n %s" % n
 
235
            n -= 1
 
236
            while n:
 
237
                #print "QHashData::firstNode() in while, n %s" % n;
 
238
                if bucket != e:
 
239
                    #print "QHashData::firstNode() in while, return *bucket %s" % bucket
 
240
                    return bucket
 
241
                bucketNum += 1
 
242
                bucket = self.d['buckets'][bucketNum]
 
243
                #print "QHashData::firstNode() in while, new bucket %s" % bucket
 
244
                n -= 1
 
245
            #print "QHashData::firstNode() return e %s" % e
 
246
            return e
 
247
 
 
248
 
 
249
        def nextNode (self, node):
 
250
            "Get the nextNode after the current, see also QHashData::nextNode()."
 
251
            #print "******************************** nextNode"
 
252
            e = self.d.cast(gdb.lookup_type('QHashData::Node').pointer())
 
253
 
 
254
            #print "nextNode: node %s" % node
 
255
 
 
256
            next = node['next']
 
257
            #print "nextNode: next %s" % next
 
258
            if next['next']:
 
259
                #return "nextNode: return next"
 
260
                return next
 
261
 
 
262
            #print "nextNode: node->h %s" % node['h']
 
263
            #print "nextNode: numBuckets %s" % self.d['numBuckets']
 
264
            start = (node['h'] % self.d['numBuckets']) + 1
 
265
            bucketNum = start
 
266
            #print "nextNode: start %s" % start
 
267
            bucket = self.d['buckets'][start]
 
268
            #print "nextNode: bucket %s" % bucket
 
269
            n = self.d['numBuckets'] - start
 
270
            #print "nextNode: n %s" % n
 
271
            while n:
 
272
                #print "nextNode: in while; n %s" % n
 
273
                #print "nextNode: in while; e %s" % e
 
274
                #print "nextNode: in while; *bucket %s" % bucket
 
275
                if bucket != e:
 
276
                    #print "nextNode: in while; return bucket %s" % bucket
 
277
                    return bucket
 
278
                bucketNum += 1
 
279
                bucket = self.d['buckets'][bucketNum]
 
280
                n -= 1
 
281
            #print "nextNode: return e %s" % e
 
282
            return e
 
283
 
 
284
        def next(self):
 
285
            "GDB iteration, first call returns key, second value and then jumps to the next hash node."
 
286
            if self.data_node == self.end_node:
 
287
                raise StopIteration
 
288
 
 
289
            node = self.hashNode()
 
290
 
 
291
            if self.count % 2 == 0:
 
292
                item = node['key']
 
293
            else:
 
294
                item = node['value']
 
295
                self.data_node = self.nextNode(self.data_node)
 
296
 
 
297
            self.count = self.count + 1
 
298
            return ('[%d]' % self.count, item)
 
299
 
 
300
    def __init__(self, val):
 
301
        self.val = val
 
302
 
 
303
    def children(self):
 
304
        return self._iterator(self.val)
 
305
 
 
306
    def to_string(self):
 
307
        if self.val['d']['size'] == 0:
 
308
            empty = "empty "
 
309
        else:
 
310
            empty = ""
 
311
 
 
312
        return "%sQMap<%s, %s>" % ( empty , self.val.type.template_argument(0), self.val.type.template_argument(1) )
 
313
 
 
314
    def display_hint (self):
 
315
        return 'map'
 
316
 
 
317
class QDatePrinter:
 
318
 
 
319
    def __init__(self, val):
 
320
        self.val = val
 
321
 
 
322
    def to_string(self):
 
323
        julianDay = self.val['jd']
 
324
 
 
325
        if julianDay == 0:
 
326
            return "invalid QDate"
 
327
 
 
328
        # Copied from Qt sources
 
329
        if julianDay >= 2299161:
 
330
            # Gregorian calendar starting from October 15, 1582
 
331
            # This algorithm is from Henry F. Fliegel and Thomas C. Van Flandern
 
332
            ell = julianDay + 68569;
 
333
            n = (4 * ell) / 146097;
 
334
            ell = ell - (146097 * n + 3) / 4;
 
335
            i = (4000 * (ell + 1)) / 1461001;
 
336
            ell = ell - (1461 * i) / 4 + 31;
 
337
            j = (80 * ell) / 2447;
 
338
            d = ell - (2447 * j) / 80;
 
339
            ell = j / 11;
 
340
            m = j + 2 - (12 * ell);
 
341
            y = 100 * (n - 49) + i + ell;
 
342
        else:
 
343
            # Julian calendar until October 4, 1582
 
344
            # Algorithm from Frequently Asked Questions about Calendars by Claus Toendering
 
345
            julianDay += 32082;
 
346
            dd = (4 * julianDay + 3) / 1461;
 
347
            ee = julianDay - (1461 * dd) / 4;
 
348
            mm = ((5 * ee) + 2) / 153;
 
349
            d = ee - (153 * mm + 2) / 5 + 1;
 
350
            m = mm + 3 - 12 * (mm / 10);
 
351
            y = dd - 4800 + (mm / 10);
 
352
            if y <= 0:
 
353
                --y;
 
354
        return "%d-%02d-%02d" % (y, m, d)
 
355
 
 
356
class QTimePrinter:
 
357
 
 
358
    def __init__(self, val):
 
359
        self.val = val
 
360
 
 
361
    def to_string(self):
 
362
        ds = self.val['mds']
 
363
 
 
364
        if ds == -1:
 
365
            return "invalid QTime"
 
366
 
 
367
        MSECS_PER_HOUR = 3600000
 
368
        SECS_PER_MIN = 60
 
369
        MSECS_PER_MIN = 60000
 
370
 
 
371
        hour = ds / MSECS_PER_HOUR
 
372
        minute = (ds % MSECS_PER_HOUR) / MSECS_PER_MIN
 
373
        second = (ds / 1000)%SECS_PER_MIN
 
374
        msec = ds % 1000
 
375
        return "%02d:%02d:%02d.%03d" % (hour, minute, second, msec)
 
376
 
 
377
class QDateTimePrinter:
 
378
 
 
379
    def __init__(self, val):
 
380
        self.val = val
 
381
 
 
382
    def to_string(self):
 
383
        #val['d'] is a QDateTimePrivate, but for some reason casting to that doesn't work
 
384
        #so work around by manually adjusting the pointer
 
385
        date = self.val['d'].cast(gdb.lookup_type('char').pointer());
 
386
        date += gdb.lookup_type('int').sizeof #increment for QAtomicInt ref;
 
387
        date = date.cast(gdb.lookup_type('QDate').pointer()).dereference();
 
388
 
 
389
        time = self.val['d'].cast(gdb.lookup_type('char').pointer());
 
390
        time += gdb.lookup_type('int').sizeof + gdb.lookup_type('QDate').sizeof #increment for QAtomicInt ref; and QDate date;
 
391
        time = time.cast(gdb.lookup_type('QTime').pointer()).dereference();
 
392
        return "%s %s" % (date, time)
 
393
 
 
394
class QUrlPrinter:
 
395
 
 
396
    def __init__(self, val):
 
397
        self.val = val
 
398
 
 
399
    def to_string(self):
 
400
        try:
 
401
            return self.val['d']['encodedOriginal']
 
402
        except RuntimeError, error:
 
403
            #if no debug information is avaliable for Qt, try guessing the correct address for encodedOriginal
 
404
            #problem with this is that if QUrlPrivate members get changed, this fails
 
405
            offset = gdb.lookup_type('int').sizeof
 
406
            offset += offset % gdb.lookup_type('void').pointer().sizeof #alignment
 
407
            offset += gdb.lookup_type('QString').sizeof * 6
 
408
            offset += gdb.lookup_type('QByteArray').sizeof
 
409
            encodedOriginal = self.val['d'].cast(gdb.lookup_type('char').pointer());
 
410
            encodedOriginal += offset
 
411
            encodedOriginal = encodedOriginal.cast(gdb.lookup_type('QByteArray').pointer()).dereference();
 
412
            encodedOriginal = encodedOriginal['d']['data'].string()
 
413
            return encodedOriginal
 
414
 
 
415
class QSetPrinter:
 
416
    "Print a QSet"
 
417
 
 
418
    def __init__(self, val):
 
419
        self.val = val
 
420
 
 
421
    class _iterator:
 
422
        def __init__(self, hashIterator):
 
423
            self.hashIterator = hashIterator
 
424
            self.count = 0
 
425
 
 
426
        def __iter__(self):
 
427
            return self
 
428
 
 
429
        def next(self):
 
430
            if self.hashIterator.data_node == self.hashIterator.end_node:
 
431
                raise StopIteration
 
432
 
 
433
            node = self.hashIterator.hashNode()
 
434
 
 
435
            item = node['key']
 
436
            self.hashIterator.data_node = self.hashIterator.nextNode(self.hashIterator.data_node)
 
437
 
 
438
            self.count = self.count + 1
 
439
            return ('[%d]' % (self.count-1), item)
 
440
 
 
441
    def children(self):
 
442
        hashPrinter = QHashPrinter(self.val['q_hash'])
 
443
        hashIterator = hashPrinter._iterator(self.val['q_hash'])
 
444
        return self._iterator(hashIterator)
 
445
 
 
446
    def to_string(self):
 
447
        return 'QSet'
 
448
 
 
449
 
 
450
class QCharPrinter:
 
451
 
 
452
    def __init__(self, val):
 
453
        self.val = val
 
454
 
 
455
    def to_string(self):
 
456
        return unichr(self.val['ucs'])
 
457
 
 
458
    def display_hint (self):
 
459
        return 'string'
 
460
 
 
461
def register_qt4_printers (obj):
 
462
    if obj == None:
 
463
        obj = gdb
 
464
 
 
465
    obj.pretty_printers.append (lookup_function)
 
466
 
 
467
def lookup_function (val):
 
468
    "Look-up and return a pretty-printer that can print val."
 
469
 
 
470
    # Get the type.
 
471
    type = val.type;
 
472
 
 
473
    # If it points to a reference, get the reference.
 
474
    if type.code == gdb.TYPE_CODE_REF:
 
475
        type = type.target ()
 
476
 
 
477
    # Get the unqualified type, stripped of typedefs.
 
478
    type = type.unqualified ().strip_typedefs ()
 
479
 
 
480
    # Get the type name.
 
481
    typename = type.tag
 
482
    if typename == None:
 
483
        return None
 
484
 
 
485
    # Iterate over local dictionary of types to determine
 
486
    # if a printer is registered for that type.  Return an
 
487
    # instantiation of the printer if found.
 
488
    for function in pretty_printers_dict:
 
489
        if function.search (typename):
 
490
            return pretty_printers_dict[function] (val)
 
491
 
 
492
    # Cannot find a pretty printer.  Return None.
 
493
    return None
 
494
 
 
495
def build_dictionary ():
 
496
    pretty_printers_dict[re.compile('^QString$')] = lambda val: QStringPrinter(val)
 
497
    pretty_printers_dict[re.compile('^QByteArray$')] = lambda val: QByteArrayPrinter(val)
 
498
    pretty_printers_dict[re.compile('^QList<.*>$')] = lambda val: QListPrinter(val, None)
 
499
    pretty_printers_dict[re.compile('^QStringList$')] = lambda val: QListPrinter(val, 'QString')
 
500
    pretty_printers_dict[re.compile('^QMap<.*>$')] = lambda val: QMapPrinter(val)
 
501
    pretty_printers_dict[re.compile('^QHash<.*>$')] = lambda val: QHashPrinter(val)
 
502
    pretty_printers_dict[re.compile('^QDate$')] = lambda val: QDatePrinter(val)
 
503
    pretty_printers_dict[re.compile('^QTime$')] = lambda val: QTimePrinter(val)
 
504
    pretty_printers_dict[re.compile('^QDateTime$')] = lambda val: QDateTimePrinter(val)
 
505
    pretty_printers_dict[re.compile('^QUrl$')] = lambda val: QUrlPrinter(val)
 
506
    pretty_printers_dict[re.compile('^QSet<.*>$')] = lambda val: QSetPrinter(val)
 
507
    pretty_printers_dict[re.compile('^QChar$')] = lambda val: QCharPrinter(val)
 
508
 
 
509
 
 
510
pretty_printers_dict = {}
 
511
 
 
512
build_dictionary ()