File indexing completed on 2024-05-12 04:39:56

0001 # -*- coding: iso-8859-1 -*-
0002 # Pretty-printers for Qt 4 and Qt 5.
0003 
0004 # SPDX-FileCopyrightText: 2009 Niko Sams <niko.sams@gmail.com>
0005 #
0006 # SPDX-License-Identifier: GPL-2.0-or-later
0007 
0008 import gdb
0009 import itertools
0010 import re
0011 import time
0012 
0013 from helper import *
0014 
0015 class QStringPrinter:
0016 
0017     def __init__(self, val):
0018         self.val = val
0019 
0020     def to_string(self):
0021         ret = ""
0022 
0023         # The QString object may not be initialized yet. In this case 'size' is a bogus value
0024         # or in case of Qt5, 'd' is an invalid pointer and the following lines might throw memory
0025         # access error. Hence the try/catch.
0026         try:
0027             size = self.val['d']['size']
0028             if size == 0:
0029                 return ret
0030             isQt4 = has_field(self.val['d'], 'data') # Qt4 has d->data, Qt5 doesn't.
0031             isQt6 = has_field(self.val['d'], 'ptr') # Qt6 has d->ptr, Qt5 doesn't.
0032             if isQt4:
0033                 dataAsCharPointer = self.val['d']['data'].cast(gdb.lookup_type("char").pointer())
0034             elif isQt6:
0035                 dataAsCharPointer = self.val['d']['ptr'].cast(gdb.lookup_type("char").pointer())
0036             else:
0037                 dataAsCharPointer = (self.val['d'] + 1).cast(gdb.lookup_type("char").pointer())
0038             ret = dataAsCharPointer.string(encoding = 'UTF-16', length = size * 2)
0039         except Exception:
0040             # swallow the exception and return empty string
0041             pass
0042         return ret
0043 
0044     def display_hint (self):
0045         return 'string'
0046 
0047 class QByteArrayPrinter:
0048 
0049     def __init__(self, val):
0050         self.val = val
0051         # Qt4 has 'data', Qt5 doesn't
0052         self.isQt4 = has_field(self.val['d'], 'data')
0053         # Qt6 has d.ptr, Qt5 doesn't
0054         self.isQt6 = has_field(self.val['d'], 'ptr')
0055 
0056     class _iterator(Iterator):
0057         def __init__(self, data, size):
0058             self.data = data
0059             self.size = size
0060             self.count = 0
0061 
0062         def __iter__(self):
0063             return self
0064 
0065         def __next__(self):
0066             if self.count >= self.size:
0067                 raise StopIteration
0068             count = self.count
0069             self.count = self.count + 1
0070             return ('[%d]' % count, self.data[count])
0071 
0072     def stringData(self):
0073         if self.isQt4:
0074             return self.val['d']['data']
0075         elif self.isQt6:
0076             return self.val['d']['ptr'].cast(gdb.lookup_type("char").pointer())
0077         else:
0078             return self.val['d'].cast(gdb.lookup_type("char").const().pointer()) + self.val['d']['offset']
0079 
0080     def children(self):
0081         return self._iterator(self.stringData(), self.val['d']['size'])
0082 
0083     def to_string(self):
0084         #todo: handle charset correctly
0085         return self.stringData()
0086 
0087     def display_hint (self):
0088         return 'string'
0089 
0090 class QListPrinter:
0091     "Print a QList"
0092 
0093     class _iterator(Iterator):
0094         def __init__(self, nodetype, d):
0095             self.nodetype = nodetype
0096             self.d = d
0097             self.count = 0
0098 
0099             #from QTypeInfo::isLarge
0100             isLarge = self.nodetype.sizeof > gdb.lookup_type('void').pointer().sizeof
0101 
0102             isPointer = self.nodetype.code == gdb.TYPE_CODE_PTR
0103 
0104             #unfortunately we can't use QTypeInfo<T>::isStatic as it's all inlined, so use
0105             #this list of types that use Q_DECLARE_TYPEINFO(T, Q_MOVABLE_TYPE)
0106             #(obviously it won't work for custom types)
0107             movableTypes = ['QRect', 'QRectF', 'QString', 'QMargins', 'QLocale', 'QChar', 'QDate', 'QTime', 'QDateTime', 'QVector',
0108                'QRegExpr', 'QPoint', 'QPointF', 'QByteArray', 'QSize', 'QSizeF', 'QBitArray', 'QLine', 'QLineF', 'QModelIndex', 'QPersitentModelIndex',
0109                'QVariant', 'QFileInfo', 'QUrl', 'QXmlStreamAttribute', 'QXmlStreamNamespaceDeclaration', 'QXmlStreamNotationDeclaration',
0110                'QXmlStreamEntityDeclaration', 'QPair<int, int>']
0111             #this list of types that use Q_DECLARE_TYPEINFO(T, Q_PRIMITIVE_TYPE) (from qglobal.h)
0112             primitiveTypes = ['bool', 'char', 'signed char', 'unsigned char', 'short', 'unsigned short', 'int', 'unsigned int', 'long', 'unsigned long', 'long long', 'unsigned long long', 'float', 'double']
0113 
0114             if movableTypes.count(self.nodetype.tag) or primitiveTypes.count(str(self.nodetype)):
0115                isStatic = False
0116             else:
0117                isStatic = not isPointer
0118 
0119             self.externalStorage = isLarge or isStatic #see QList::Node::t()
0120 
0121 
0122         def __iter__(self):
0123             return self
0124 
0125         def __next__(self):
0126             isQt6 = has_field(self.d, 'size')
0127             if isQt6:
0128                 size = self.d['size']
0129             else:
0130                 size = self.d['end'] - self.d['begin']
0131 
0132             if self.count >= size:
0133                 raise StopIteration
0134             count = self.count
0135 
0136             if isQt6:
0137                 value = self.d['ptr'] + count
0138             else:
0139                 array = self.d['array'].address + self.d['begin'] + count
0140                 if self.externalStorage:
0141                     value = array.cast(gdb.lookup_type('QList<%s>::Node' % self.nodetype).pointer())['v']
0142                 else:
0143                     value = array
0144             self.count = self.count + 1
0145             return ('[%d]' % count, value.cast(self.nodetype.pointer()).dereference())
0146 
0147     def __init__(self, val, container, itype):
0148         self.d = val['d']
0149         self.container = container
0150         self.isQt6 = has_field(self.d, 'size')
0151 
0152         if self.isQt6:
0153             self.size = self.d['size']
0154         else:
0155             self.size = self.d['end'] - self.d['begin']
0156 
0157         if itype == None:
0158             self.itype = val.type.template_argument(0)
0159         else:
0160             self.itype = gdb.lookup_type(itype)
0161 
0162     def children(self):
0163         return self._iterator(self.itype, self.d)
0164 
0165     def to_string(self):
0166         return "%s<%s> (size = %s)" % ( self.container, self.itype, self.size )
0167 
0168 class QVectorPrinter:
0169     "Print a QVector"
0170 
0171     class _iterator(Iterator):
0172         def __init__(self, nodetype, data, size):
0173             self.nodetype = nodetype
0174             self.data = data
0175             self.size = size
0176             self.count = 0
0177 
0178         def __iter__(self):
0179             return self
0180 
0181         def __next__(self):
0182             if self.count >= self.size:
0183                 raise StopIteration
0184             count = self.count
0185 
0186             self.count = self.count + 1
0187             return ('[%d]' % count, self.data[count])
0188 
0189     def __init__(self, val, container):
0190         self.val = val
0191         self.container = container
0192         self.itype = self.val.type.template_argument(0)
0193 
0194     def children(self):
0195         isQt4 = has_field(self.val['d'], 'p') # Qt4 has 'p', Qt5 doesn't
0196         if isQt4:
0197             return self._iterator(self.itype, self.val['p']['array'], self.val['p']['size'])
0198         else:
0199             data = self.val['d'].cast(gdb.lookup_type("char").const().pointer()) + self.val['d']['offset']
0200             return self._iterator(self.itype, data.cast(self.itype.pointer()), self.val['d']['size'])
0201 
0202     def to_string(self):
0203         size = self.val['d']['size']
0204 
0205         return "%s<%s> (size = %s)" % ( self.container, self.itype, size )
0206 
0207 class QLinkedListPrinter:
0208     "Print a QLinkedList"
0209 
0210     class _iterator(Iterator):
0211         def __init__(self, nodetype, begin, size):
0212             self.nodetype = nodetype
0213             self.it = begin
0214             self.pos = 0
0215             self.size = size
0216 
0217         def __iter__(self):
0218             return self
0219 
0220         def __next__(self):
0221             if self.pos >= self.size:
0222                 raise StopIteration
0223 
0224             pos = self.pos
0225             val = self.it['t']
0226             self.it = self.it['n']
0227             self.pos = self.pos + 1
0228             return ('[%d]' % pos, val)
0229 
0230     def __init__(self, val):
0231         self.val = val
0232         self.itype = self.val.type.template_argument(0)
0233 
0234     def children(self):
0235         return self._iterator(self.itype, self.val['e']['n'], self.val['d']['size'])
0236 
0237     def to_string(self):
0238         size = self.val['d']['size']
0239 
0240         return "QLinkedList<%s> (size = %s)" % ( self.itype, size )
0241 
0242 class QMapPrinter:
0243     "Print a QMap"
0244 
0245     class _iteratorQt4(Iterator):
0246         def __init__(self, val):
0247             self.val = val
0248             self.ktype = self.val.type.template_argument(0)
0249             self.vtype = self.val.type.template_argument(1)
0250             self.data_node = self.val['e']['forward'][0]
0251             self.count = 0
0252 
0253         def __iter__(self):
0254             return self
0255 
0256         def payload (self):
0257             if gdb.parse_and_eval:
0258                 ret = int(gdb.parse_and_eval('QMap<%s, %s>::payload()' % (self.ktype, self.vtype)))
0259                 if (ret): return ret;
0260 
0261             #if the inferior function call didn't work, let's try to calculate ourselves
0262 
0263             #we can't use QMapPayloadNode as it's inlined
0264             #as a workaround take the sum of sizeof(members)
0265             ret = self.ktype.sizeof
0266             ret += self.vtype.sizeof
0267             ret += gdb.lookup_type('void').pointer().sizeof
0268 
0269             #but because of data alignment the value can be higher
0270             #so guess it's aliged by sizeof(void*)
0271             #TODO: find a real solution for this problem
0272             ret += ret % gdb.lookup_type('void').pointer().sizeof
0273 
0274             #for some reason booleans are different
0275             if str(self.vtype) == 'bool':
0276                 ret += 2
0277 
0278             ret -= gdb.lookup_type('void').pointer().sizeof
0279 
0280             return ret
0281 
0282         def concrete (self, data_node):
0283             node_type = gdb.lookup_type('QMapNode<%s, %s>' % (self.ktype, self.vtype)).pointer()
0284             return (data_node.cast(gdb.lookup_type('char').pointer()) - self.payload()).cast(node_type)
0285 
0286         def __next__(self):
0287             if self.data_node == self.val['e']:
0288                 raise StopIteration
0289             node = self.concrete(self.data_node).dereference()
0290             if self.count % 2 == 0:
0291                 item = node['key']
0292             else:
0293                 item = node['value']
0294                 self.data_node = node['forward'][0]
0295 
0296             result = ('[%d]' % self.count, item)
0297             self.count = self.count + 1
0298             return result
0299 
0300     class _iteratorQt5:
0301         def __init__(self, val):
0302             realtype = val.type.strip_typedefs()
0303             keytype = realtype.template_argument(0)
0304             valtype = realtype.template_argument(1)
0305             node_type = gdb.lookup_type('QMapData<' + keytype.name + ',' + valtype.name + '>::Node')
0306             self.node_p_type = node_type.pointer()
0307             self.root = val['d']['header']
0308             self.current = None
0309             self.next_is_key = True
0310             self.i = -1
0311             # we store the path here to avoid keeping re-fetching
0312             # values from the inferior (also, skips the pointer
0313             # arithmetic involved in using the parent pointer)
0314             self.path = []
0315 
0316         def __iter__(self):
0317             return self
0318 
0319         def moveToNextNode(self):
0320             if self.current is None:
0321                 # find the leftmost node
0322                 if not self.root['left']:
0323                     return False
0324                 self.current = self.root
0325                 while self.current['left']:
0326                     self.path.append(self.current)
0327                     self.current = self.current['left']
0328             elif self.current['right']:
0329                 self.path.append(self.current)
0330                 self.current = self.current['right']
0331                 while self.current['left']:
0332                     self.path.append(self.current)
0333                     self.current = self.current['left']
0334             else:
0335                 last = self.current
0336                 self.current = self.path.pop()
0337                 while self.current['right'] == last:
0338                     last = self.current
0339                     self.current = self.path.pop()
0340                 # if there are no more parents, we are at the root
0341                 if len(self.path) == 0:
0342                     return False
0343             return True
0344 
0345         def __next__(self):
0346             if self.next_is_key:
0347                 if not self.moveToNextNode():
0348                     raise StopIteration
0349                 self.current_typed = self.current.reinterpret_cast(self.node_p_type)
0350                 self.next_is_key = False
0351                 self.i += 1
0352                 return ('key' + str(self.i), self.current_typed['key'])
0353             else:
0354                 self.next_is_key = True
0355                 return ('value' + str(self.i), self.current_typed['value'])
0356 
0357         def next(self):
0358             return self.__next__()
0359 
0360     def __init__(self, val, container):
0361         self.val = val
0362         self.container = container
0363 
0364     def children(self):
0365         if self.val['d']['size'] == 0:
0366             return []
0367 
0368         isQt4 = has_field(self.val, 'e') # Qt4 has 'e', Qt5 doesn't
0369         if isQt4:
0370             return self._iteratorQt4(self.val)
0371         else:
0372             return self._iteratorQt5(self.val)
0373 
0374     def to_string(self):
0375         size = self.val['d']['size']
0376 
0377         return "%s<%s, %s> (size = %s)" % ( self.container, self.val.type.template_argument(0), self.val.type.template_argument(1), size )
0378 
0379     def display_hint (self):
0380         return 'map'
0381 
0382 class QHashPrinter:
0383     "Print a QHash"
0384 
0385     class _iterator(Iterator):
0386         def __init__(self, val):
0387             self.val = val
0388             self.d = self.val['d']
0389             self.ktype = self.val.type.template_argument(0)
0390             self.vtype = self.val.type.template_argument(1)
0391             self.end_node = self.d.cast(gdb.lookup_type('QHashData::Node').pointer())
0392             self.data_node = self.firstNode()
0393             self.count = 0
0394 
0395         def __iter__(self):
0396             return self
0397 
0398         def hashNode (self):
0399             "Casts the current QHashData::Node to a QHashNode and returns the result. See also QHash::concrete()"
0400             return self.data_node.cast(gdb.lookup_type('QHashNode<%s, %s>' % (self.ktype, self.vtype)).pointer())
0401 
0402         def firstNode (self):
0403             "Get the first node, See QHashData::firstNode()."
0404             e = self.d.cast(gdb.lookup_type('QHashData::Node').pointer())
0405             #print "QHashData::firstNode() e %s" % e
0406             bucketNum = 0
0407             bucket = self.d['buckets'][bucketNum]
0408             #print "QHashData::firstNode() *bucket %s" % bucket
0409             n = self.d['numBuckets']
0410             #print "QHashData::firstNode() n %s" % n
0411             while n:
0412                 #print "QHashData::firstNode() in while, n %s" % n;
0413                 if bucket != e:
0414                     #print "QHashData::firstNode() in while, return *bucket %s" % bucket
0415                     return bucket
0416                 bucketNum += 1
0417                 bucket = self.d['buckets'][bucketNum]
0418                 #print "QHashData::firstNode() in while, new bucket %s" % bucket
0419                 n -= 1
0420             #print "QHashData::firstNode() return e %s" % e
0421             return e
0422 
0423 
0424         def nextNode (self, node):
0425             "Get the nextNode after the current, see also QHashData::nextNode()."
0426             #print "******************************** nextNode"
0427             #print "nextNode: node %s" % node
0428             next = node['next'].cast(gdb.lookup_type('QHashData::Node').pointer())
0429             e = next
0430 
0431             #print "nextNode: next %s" % next
0432             if next['next']:
0433                 #print "nextNode: return next"
0434                 return next
0435 
0436             #print "nextNode: node->h %s" % node['h']
0437             #print "nextNode: numBuckets %s" % self.d['numBuckets']
0438             start = (node['h'] % self.d['numBuckets']) + 1
0439             bucketNum = start
0440             #print "nextNode: start %s" % start
0441             bucket = self.d['buckets'][start]
0442             #print "nextNode: bucket %s" % bucket
0443             n = self.d['numBuckets'] - start
0444             #print "nextNode: n %s" % n
0445             while n:
0446                 #print "nextNode: in while; n %s" % n
0447                 #print "nextNode: in while; e %s" % e
0448                 #print "nextNode: in while; *bucket %s" % bucket
0449                 if bucket != e:
0450                     #print "nextNode: in while; return bucket %s" % bucket
0451                     return bucket
0452                 bucketNum += 1
0453                 bucket = self.d['buckets'][bucketNum]
0454                 n -= 1
0455             #print "nextNode: return e %s" % e
0456             return e
0457 
0458         def __next__(self):
0459             "GDB iteration, first call returns key, second value and then jumps to the next hash node."
0460             if self.data_node == self.end_node:
0461                 raise StopIteration
0462 
0463             node = self.hashNode()
0464 
0465             if self.count % 2 == 0:
0466                 item = node['key']
0467             else:
0468                 item = node['value']
0469                 self.data_node = self.nextNode(self.data_node)
0470 
0471             self.count = self.count + 1
0472             return ('[%d]' % self.count, item)
0473 
0474     def __init__(self, val, container):
0475         self.val = val
0476         self.container = container
0477 
0478     def children(self):
0479         return self._iterator(self.val)
0480 
0481     def to_string(self):
0482         size = self.val['d']['size']
0483 
0484         return "%s<%s, %s> (size = %s)" % ( self.container, self.val.type.template_argument(0), self.val.type.template_argument(1), size )
0485 
0486     def display_hint (self):
0487         return 'map'
0488 
0489 class QDatePrinter:
0490 
0491     def __init__(self, val):
0492         self.val = val
0493 
0494     def to_string(self):
0495         julianDay = self.val['jd']
0496 
0497         if julianDay == 0:
0498             return "invalid QDate"
0499 
0500         # Copied from Qt sources
0501         if julianDay >= 2299161:
0502             # Gregorian calendar starting from October 15, 1582
0503             # This algorithm is from Henry F. Fliegel and Thomas C. Van Flandern
0504             ell = julianDay + 68569;
0505             n = (4 * ell) / 146097;
0506             ell = ell - (146097 * n + 3) / 4;
0507             i = (4000 * (ell + 1)) / 1461001;
0508             ell = ell - (1461 * i) / 4 + 31;
0509             j = (80 * ell) / 2447;
0510             d = ell - (2447 * j) / 80;
0511             ell = j / 11;
0512             m = j + 2 - (12 * ell);
0513             y = 100 * (n - 49) + i + ell;
0514         else:
0515             # Julian calendar until October 4, 1582
0516             # Algorithm from Frequently Asked Questions about Calendars by Claus Toendering
0517             julianDay += 32082;
0518             dd = (4 * julianDay + 3) / 1461;
0519             ee = julianDay - (1461 * dd) / 4;
0520             mm = ((5 * ee) + 2) / 153;
0521             d = ee - (153 * mm + 2) / 5 + 1;
0522             m = mm + 3 - 12 * (mm / 10);
0523             y = dd - 4800 + (mm / 10);
0524             if y <= 0:
0525                 --y;
0526         return "%d-%02d-%02d" % (y, m, d)
0527 
0528 class QTimePrinter:
0529 
0530     def __init__(self, val):
0531         self.val = val
0532 
0533     def to_string(self):
0534         ds = self.val['mds']
0535 
0536         if ds == -1:
0537             return "invalid QTime"
0538 
0539         MSECS_PER_HOUR = 3600000
0540         SECS_PER_MIN = 60
0541         MSECS_PER_MIN = 60000
0542 
0543         hour = ds / MSECS_PER_HOUR
0544         minute = (ds % MSECS_PER_HOUR) / MSECS_PER_MIN
0545         second = (ds / 1000)%SECS_PER_MIN
0546         msec = ds % 1000
0547         return "%02d:%02d:%02d.%03d" % (hour, minute, second, msec)
0548 
0549 class QDateTimePrinter:
0550 
0551     def __init__(self, val):
0552         self.val = val
0553 
0554     def to_string(self):
0555         time_t = gdb.parse_and_eval("reinterpret_cast<const QDateTime*>(%s)->toSecsSinceEpoch()" % self.val.address)
0556         return time.ctime(int(time_t))
0557 
0558 class QUrlPrinter:
0559 
0560     def __init__(self, val):
0561         self.val = val
0562 
0563     def to_string(self):
0564         # first try to access the Qt 5 data
0565         try:
0566             int_type = gdb.lookup_type('int')
0567             string_type = gdb.lookup_type('QString')
0568             string_pointer = string_type.pointer()
0569 
0570             addr = self.val['d'].cast(gdb.lookup_type('char').pointer())
0571             if not addr:
0572                 return "<invalid>"
0573             # skip QAtomicInt ref
0574             addr += int_type.sizeof
0575             # handle int port
0576             port = addr.cast(int_type.pointer()).dereference()
0577             addr += int_type.sizeof
0578             # handle QString scheme
0579             scheme = QStringPrinter(addr.cast(string_pointer).dereference()).to_string()
0580             addr += string_type.sizeof
0581             # handle QString username
0582             username = QStringPrinter(addr.cast(string_pointer).dereference()).to_string()
0583             addr += string_type.sizeof
0584             # skip QString password
0585             addr += string_type.sizeof
0586             # handle QString host
0587             host = QStringPrinter(addr.cast(string_pointer).dereference()).to_string()
0588             addr += string_type.sizeof
0589             # handle QString path
0590             path = QStringPrinter(addr.cast(string_pointer).dereference()).to_string()
0591             addr += string_type.sizeof
0592             # handle QString query
0593             query = QStringPrinter(addr.cast(string_pointer).dereference()).to_string()
0594             addr += string_type.sizeof
0595             # handle QString fragment
0596             fragment = QStringPrinter(addr.cast(string_pointer).dereference()).to_string()
0597 
0598             url = ""
0599             if len(scheme) > 0:
0600                 # TODO: always adding // is apparently not compliant in all cases
0601                 url += scheme + "://"
0602             if len(host) > 0:
0603                 if len(username) > 0:
0604                     url += username + "@"
0605                 url += host
0606                 if port != -1:
0607                     url += ":" + str(port)
0608             url += path
0609             if len(query) > 0:
0610                 url += "?" + query
0611             if len(fragment) > 0:
0612                 url += "#" + fragment
0613 
0614             return url
0615         except:
0616             pass
0617         # then try to print directly, but that might lead to issues (see http://sourceware-org.1504.n7.nabble.com/help-Calling-malloc-from-a-Python-pretty-printer-td284031.html)
0618         try:
0619             return gdb.parse_and_eval("reinterpret_cast<const QUrl*>(%s)->toString((QUrl::FormattingOptions)QUrl::PrettyDecoded)" % self.val.address)
0620         except:
0621             pass
0622         # if everything fails, maybe we deal with Qt 4 code
0623         try:
0624             return self.val['d']['encodedOriginal']
0625         except RuntimeError:
0626             #if no debug information is available for Qt, try guessing the correct address for encodedOriginal
0627             #problem with this is that if QUrlPrivate members get changed, this fails
0628             offset = gdb.lookup_type('int').sizeof
0629             offset += offset % gdb.lookup_type('void').pointer().sizeof #alignment
0630             offset += gdb.lookup_type('QString').sizeof * 6
0631             offset += gdb.lookup_type('QByteArray').sizeof
0632             encodedOriginal = self.val['d'].cast(gdb.lookup_type('char').pointer());
0633             encodedOriginal += offset
0634             encodedOriginal = encodedOriginal.cast(gdb.lookup_type('QByteArray').pointer()).dereference();
0635             encodedOriginal = encodedOriginal['d']['data'].string()
0636             return encodedOriginal
0637 
0638 class QSetPrinter:
0639     "Print a QSet"
0640 
0641     def __init__(self, val):
0642         self.val = val
0643 
0644     class _iterator(Iterator):
0645         def __init__(self, hashIterator):
0646             self.hashIterator = hashIterator
0647             self.count = 0
0648 
0649         def __iter__(self):
0650             return self
0651 
0652         def __next__(self):
0653             if self.hashIterator.data_node == self.hashIterator.end_node:
0654                 raise StopIteration
0655 
0656             node = self.hashIterator.hashNode()
0657 
0658             item = node['key']
0659             self.hashIterator.data_node = self.hashIterator.nextNode(self.hashIterator.data_node)
0660 
0661             self.count = self.count + 1
0662             return ('[%d]' % (self.count-1), item)
0663 
0664     def children(self):
0665         hashPrinter = QHashPrinter(self.val['q_hash'], None)
0666         hashIterator = hashPrinter._iterator(self.val['q_hash'])
0667         return self._iterator(hashIterator)
0668 
0669     def to_string(self):
0670         size = self.val['q_hash']['d']['size']
0671 
0672         return "QSet<%s> (size = %s)" % ( self.val.type.template_argument(0), size )
0673 
0674 
0675 class QCharPrinter:
0676 
0677     def __init__(self, val):
0678         self.val = val
0679 
0680     def to_string(self):
0681         return unichr(self.val['ucs'])
0682 
0683     def display_hint (self):
0684         return 'string'
0685 
0686 class QUuidPrinter:
0687 
0688     def __init__(self, val):
0689         self.val = val
0690 
0691     def to_string(self):
0692         return "QUuid({%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x})" % (int(self.val['data1']), int(self.val['data2']), int(self.val['data3']),
0693                                             int(self.val['data4'][0]), int(self.val['data4'][1]),
0694                                             int(self.val['data4'][2]), int(self.val['data4'][3]),
0695                                             int(self.val['data4'][4]), int(self.val['data4'][5]),
0696                                             int(self.val['data4'][6]), int(self.val['data4'][7]))
0697 
0698     def display_hint (self):
0699         return 'string'
0700 
0701 class QVariantPrinter:
0702 
0703     def __init__(self, val):
0704         self.val = val
0705 
0706     def to_string(self):
0707         d = self.val['d']
0708 
0709         if d['is_null']:
0710             return "QVariant(NULL)"
0711 
0712         data_type = d['type']
0713         type_str = ("type = %d" % data_type)
0714         try:
0715             typeAsCharPointer = (gdb.parse_and_eval("QVariant::typeToName(%d)" % data_type).cast(gdb.lookup_type("char").pointer()))
0716             if typeAsCharPointer:
0717                 type_str = typeAsCharPointer.string(encoding = 'UTF-8')
0718         except Exception as e:
0719             pass
0720 
0721         data = d['data']
0722         is_shared = d['is_shared']
0723         value_str = ""
0724         if is_shared:
0725             private_shared = data['shared'].dereference()
0726             value_str = "PrivateShared(%s)" % hex(private_shared['ptr'])
0727         elif type_str.startswith("type = "):
0728             value_str = str(data['ptr'])
0729         else:
0730             type_obj = None
0731             try:
0732                 type_obj = gdb.lookup_type(type_str)
0733             except Exception as e:
0734                 value_str = str(data['ptr'])
0735             if type_obj:
0736                 if type_obj.sizeof > type_obj.pointer().sizeof:
0737                     value_ptr = data['ptr'].reinterpret_cast(type_obj.const().pointer())
0738                     value_str = str(value_ptr.dereference())
0739                 else: 
0740                     value_ptr = data['c'].address.reinterpret_cast(type_obj.const().pointer())
0741                     value_str = str(value_ptr.dereference())
0742 
0743         return "QVariant(%s, %s)" % (type_str, value_str)
0744 
0745 pretty_printers_dict = {}
0746 
0747 def register_qt_printers (obj):
0748     if obj == None:
0749         obj = gdb
0750 
0751     obj.pretty_printers.append(FunctionLookup(gdb, pretty_printers_dict))
0752 
0753 def build_dictionary ():
0754     pretty_printers_dict[re.compile('^QString$')] = lambda val: QStringPrinter(val)
0755     pretty_printers_dict[re.compile('^QByteArray$')] = lambda val: QByteArrayPrinter(val)
0756     pretty_printers_dict[re.compile('^QList<.*>$')] = lambda val: QListPrinter(val, 'QList', None)
0757     pretty_printers_dict[re.compile('^QStringList$')] = lambda val: QListPrinter(val, 'QStringList', 'QString')
0758     pretty_printers_dict[re.compile('^QQueue')] = lambda val: QListPrinter(val, 'QQueue', None)
0759     pretty_printers_dict[re.compile('^QVector<.*>$')] = lambda val: QVectorPrinter(val, 'QVector')
0760     pretty_printers_dict[re.compile('^QStack<.*>$')] = lambda val: QVectorPrinter(val, 'QStack')
0761     pretty_printers_dict[re.compile('^QLinkedList<.*>$')] = lambda val: QLinkedListPrinter(val)
0762     pretty_printers_dict[re.compile('^QMap<.*>$')] = lambda val: QMapPrinter(val, 'QMap')
0763     pretty_printers_dict[re.compile('^QMultiMap<.*>$')] = lambda val: QMapPrinter(val, 'QMultiMap')
0764     pretty_printers_dict[re.compile('^QHash<.*>$')] = lambda val: QHashPrinter(val, 'QHash')
0765     pretty_printers_dict[re.compile('^QMultiHash<.*>$')] = lambda val: QHashPrinter(val, 'QMultiHash')
0766     pretty_printers_dict[re.compile('^QDate$')] = lambda val: QDatePrinter(val)
0767     pretty_printers_dict[re.compile('^QTime$')] = lambda val: QTimePrinter(val)
0768     pretty_printers_dict[re.compile('^QDateTime$')] = lambda val: QDateTimePrinter(val)
0769     pretty_printers_dict[re.compile('^QUrl$')] = lambda val: QUrlPrinter(val)
0770     pretty_printers_dict[re.compile('^QSet<.*>$')] = lambda val: QSetPrinter(val)
0771     pretty_printers_dict[re.compile('^QChar$')] = lambda val: QCharPrinter(val)
0772     pretty_printers_dict[re.compile('^QUuid')] = lambda val: QUuidPrinter(val)
0773     pretty_printers_dict[re.compile('^QVariant')] = lambda val: QVariantPrinter(val)
0774 
0775 
0776 build_dictionary ()