File indexing completed on 2024-05-12 04:40:08

0001 #
0002 # LLDB data formatters for Qt types
0003 #
0004 # SPDX-FileCopyrightText: 2016 Aetf <aetf@unlimitedcodeworks.xyz>
0005 #
0006 # SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007 #
0008 
0009 from __future__ import print_function
0010 
0011 import time
0012 import datetime as dt
0013 import string
0014 from urllib.parse import urlsplit, urlunsplit
0015 
0016 import locale
0017 import lldb
0018 
0019 from helpers import (HiddenMemberProvider, quote, unquote, unichr, toSBPointer, Iterator, validAddr,
0020                      validPointer, invoke, rename, canonicalized_type_name)
0021 
0022 
0023 def __lldb_init_module(debugger, unused):
0024     debugger.HandleCommand('type synthetic add QString -w kdevelop-qt -l qt.QStringFormatter')
0025     debugger.HandleCommand('type summary add QString -w kdevelop-qt -F qt.QStringSummaryProvider')
0026 
0027     debugger.HandleCommand('type summary add QChar -w kdevelop-qt -F qt.QCharSummaryProvider')
0028 
0029     debugger.HandleCommand('type synthetic add QByteArray -w kdevelop-qt -l qt.QByteArrayFormatter')
0030     debugger.HandleCommand('type summary add QByteArray -w kdevelop-qt -e -F qt.QByteArraySummaryProvider')
0031 
0032     debugger.HandleCommand('type synthetic add -x "^QList<.+>$" -w kdevelop-qt -l qt.QListFormatter')
0033     debugger.HandleCommand('type summary add -x "^QList<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"')
0034 
0035     debugger.HandleCommand('type synthetic add QStringList -w kdevelop-qt -l qt.QStringListFormatter')
0036     debugger.HandleCommand('type summary add QStringList -w kdevelop-qt -e -s "<size=${svar%#}>"')
0037 
0038     debugger.HandleCommand('type synthetic add -x "^QQueue<.+>$" -w kdevelop-qt -l qt.QQueueFormatter')
0039     debugger.HandleCommand('type summary add -x "^QQueue<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"')
0040 
0041     debugger.HandleCommand('type synthetic add -x "^QVector<.+>$" -w kdevelop-qt -l qt.QVectorFormatter')
0042     debugger.HandleCommand('type summary add -x "^QVector<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"')
0043 
0044     debugger.HandleCommand('type synthetic add -x "^QStack<.+>$" -w kdevelop-qt -l qt.QStackFormatter')
0045     debugger.HandleCommand('type summary add -x "^QStack<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"')
0046 
0047     debugger.HandleCommand('type synthetic add -x "^QLinkedList<.+>$" -w kdevelop-qt -l qt.QLinkedListFormatter')
0048     debugger.HandleCommand('type summary add -x "^QLinkedList<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"')
0049 
0050     debugger.HandleCommand('type synthetic add -x "^QMapNode<.+>$" -w kdevelop-qt -l qt.KeyValueFormatter')
0051     debugger.HandleCommand('type summary add -x "^QMapNode<.+>$" -w kdevelop-qt -F qt.KeyValueSummaryProvider')
0052 
0053     debugger.HandleCommand('type synthetic add -x "^QMap<.+>$" -w kdevelop-qt -l qt.QMapFormatter')
0054     debugger.HandleCommand('type summary add -x "^QMap<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"')
0055 
0056     debugger.HandleCommand('type synthetic add -x "^QMultiMap<.+>$" -w kdevelop-qt -l qt.QMultiMapFormatter')
0057     debugger.HandleCommand('type summary add -x "^QMultiMap<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"')
0058 
0059     debugger.HandleCommand('type synthetic add -x "^QHashNode<.+>$" -w kdevelop-qt -l qt.KeyValueFormatter')
0060     debugger.HandleCommand('type summary add -x "^QHashNode<.+>$" -w kdevelop-qt -F qt.KeyValueSummaryProvider')
0061 
0062     debugger.HandleCommand('type synthetic add -x "^QHash<.+>$" -w kdevelop-qt -l qt.QHashFormatter')
0063     debugger.HandleCommand('type summary add -x "^QHash<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"')
0064 
0065     debugger.HandleCommand('type synthetic add -x "^QMultiHash<.+>$" -w kdevelop-qt -l qt.QMultiHashFormatter')
0066     debugger.HandleCommand('type summary add -x "^QMultiHash<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"')
0067 
0068     debugger.HandleCommand('type synthetic add -x "^QSet<.+>$" -w kdevelop-qt -l qt.QSetFormatter')
0069     debugger.HandleCommand('type summary add -x "^QSet<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"')
0070 
0071     debugger.HandleCommand('type synthetic add QDate -w kdevelop-qt -l qt.QDateFormatter')
0072     debugger.HandleCommand('type summary add QDate -w kdevelop-qt -e -F qt.QDateSummaryProvider')
0073 
0074     debugger.HandleCommand('type synthetic add QTime -w kdevelop-qt -l qt.QTimeFormatter')
0075     debugger.HandleCommand('type summary add -x QTime -w kdevelop-qt -e -F qt.QTimeSummaryProvider')
0076 
0077     debugger.HandleCommand('type synthetic add QDateTime -w kdevelop-qt -l qt.QDateTimeFormatter')
0078     debugger.HandleCommand('type summary add -x QDateTime -w kdevelop-qt -e -F qt.QDateTimeSummaryProvider')
0079 
0080     debugger.HandleCommand('type synthetic add QUrl -w kdevelop-qt -l qt.QUrlFormatter')
0081     debugger.HandleCommand('type summary add QUrl -w kdevelop-qt -e -F qt.QUrlSummaryProvider')
0082 
0083     debugger.HandleCommand('type synthetic add QUuid -w kdevelop-qt -l qt.QUuidFormatter')
0084     debugger.HandleCommand('type summary add QUuid -w kdevelop-qt -F qt.QUuidSummaryProvider')
0085 
0086     debugger.HandleCommand('type category enable kdevelop-qt')
0087 
0088 
0089 def printableQString(valobj):
0090     pointer = 0
0091     length = 0
0092     if valobj.IsValid():
0093         d = valobj.GetChildMemberWithName('d')
0094         data = d.GetChildMemberWithName('data')
0095         offset = d.GetChildMemberWithName('offset')
0096         size = d.GetChildMemberWithName('size')
0097 
0098         isQt4 = data.IsValid()
0099         size_val = size.GetValueAsSigned(-1)
0100         alloc = d.GetChildMemberWithName('alloc').GetValueAsUnsigned(0)
0101         if isQt4:
0102             alloc += 1
0103 
0104         # some sanity check to see if we are dealing with garbage
0105         if size_val < 0 or size_val >= alloc:
0106             return None, 0, 0
0107 
0108         tooLarge = u''
0109         if size_val > HiddenMemberProvider._capping_size():
0110             tooLarge = u'...'
0111             size_val = HiddenMemberProvider._capping_size()
0112 
0113         if isQt4:
0114             pointer = data.GetValueAsUnsigned(0)
0115         elif offset.IsValid():
0116                 pointer = d.GetValueAsUnsigned(0) + offset.GetValueAsUnsigned(0)
0117         else:
0118             qarraydata_t = valobj.GetTarget().FindFirstType('QArrayData')
0119             if qarraydata_t.IsValid():
0120                 pointer = d.GetValueAsUnsigned(0) + qarraydata_t.GetByteSize()
0121             else:
0122                 pointer = d.GetValueAsUnsigned(0) + 24  # Fallback to hardcoded value
0123 
0124         # size in the number of chars, each char is 2 bytes in UTF16
0125         length = size_val * 2
0126         if length == 0:
0127             return u'', pointer, length
0128 
0129         try:
0130             error = lldb.SBError()
0131             string_data = valobj.process.ReadMemory(pointer, length, error)
0132             # The QString object might be not yet initialized. In this case size is a bogus value,
0133             # and memory access may fail
0134             if error.Success():
0135                 content = string_data.decode('utf-16')
0136                 return content + tooLarge, pointer, length
0137         except:
0138             pass
0139     return None, 0, 0
0140 
0141 
0142 def QStringSummaryProvider(valobj, internal_dict):
0143     if valobj.IsValid():
0144         # content = valobj.GetChildMemberWithName('(content)')
0145         # if content.IsValid():
0146         #     try:
0147         #         error = lldb.SBError()
0148         #         rawprintable = content.GetData().GetString(error, 0)
0149         #         if error.Success():
0150         #             printable = rawprintable.decode()
0151         #             return quote(printable)
0152         #     except:
0153         #         pass
0154 
0155         # FIXME: there's no reliable way to pass data from formatter to
0156         # summary provider currently. So directly pull data from inferior
0157 
0158         # Something wrong with synthetic provider, or
0159         # no synthetic provider installed, get the content by ourselves
0160         printable, _, _ = printableQString(valobj)
0161         if printable is not None:
0162             return quote(printable)
0163     return '<Invalid>'
0164 
0165 
0166 class QStringFormatter(HiddenMemberProvider):
0167     """A lldb synthetic provider for QString"""
0168 
0169     def __init__(self, valobj, internal_dict):
0170         super(QStringFormatter, self).__init__(valobj, internal_dict)
0171         self._qchar_type = valobj.GetTarget().FindFirstType('QChar')
0172         self._qchar_size = self._qchar_type.GetByteSize()
0173 
0174     def _update(self):
0175         printable, dataPointer, byteLength = printableQString(self.valobj)
0176         strLength = int(byteLength / 2)
0177 
0178         if printable is not None:
0179             for idx in range(0, strLength):
0180                 var = self.valobj.CreateValueFromAddress('[{}]'.format(idx),
0181                                                          dataPointer + idx * self._qchar_size,
0182                                                          self._qchar_type)
0183                 self._addChild(var)
0184             self._num_children = strLength
0185 
0186 
0187 def QCharSummaryProvider(valobj, internal_dict):
0188     if valobj.IsValid():
0189         ucs = valobj.GetChildMemberWithName('ucs').GetValueAsUnsigned(0)
0190         if ucs == 39:
0191             # for '\'', python returns "'" rather than '\''
0192             return u"'\\''"
0193         else:
0194             return unichr(ucs).__repr__()[1:]
0195     return None
0196 
0197 
0198 def printableQByteArray(valobj):
0199     if valobj.IsValid():
0200         d = valobj.GetChildMemberWithName('d')
0201         data = d.GetChildMemberWithName('data')
0202         offset = d.GetChildMemberWithName('offset')
0203         size = d.GetChildMemberWithName('size')
0204 
0205         isQt4 = data.IsValid()
0206         size_val = size.GetValueAsSigned(-1)
0207         alloc = d.GetChildMemberWithName('alloc').GetValueAsUnsigned(0)
0208         if isQt4:
0209             alloc += 1
0210 
0211         # sanity check
0212         if size_val < 0 or size_val >= alloc:
0213             return None, 0, 0
0214 
0215         tooLarge = u''
0216         if size_val > HiddenMemberProvider._capping_size():
0217             tooLarge = u'...'
0218             size_val = HiddenMemberProvider._capping_size()
0219 
0220         if isQt4:
0221             pointer = data.GetValueAsUnsigned(0)
0222         elif offset.IsValid():
0223                 pointer = d.GetValueAsUnsigned(0) + offset.GetValueAsUnsigned(0)
0224         else:
0225             pointer = d.GetValueAsUnsigned(0) + 24  # Fallback to hardcoded value
0226 
0227         length = size_val
0228         if length == 0:
0229             return u'', pointer, length
0230 
0231         try:
0232             error = lldb.SBError()
0233             string_data = valobj.process.ReadMemory(pointer, length, error)
0234             # The object might be not yet initialized. In this case size is a bogus value,
0235             # and memory access may fail
0236             if error.Success():
0237                 # replace non-ascii byte with a space and get a printable version
0238                 ls = list(string_data)
0239                 for idx in range(length):
0240                     if ls[idx] in string.printable:
0241                         if ls[idx] != "'":
0242                             # convert tab, nl, ..., and '\\' to r'\\'
0243                             ls[idx] = ls[idx].__repr__()[1:-1]
0244                     else:
0245                         ls[idx] = r'\x{:02x}'.format(ord(ls[idx]))
0246                 content = u''.join(ls)
0247                 return content + tooLarge, pointer, length
0248         except:
0249             pass
0250     return None, 0, 0
0251 
0252 
0253 def QByteArraySummaryProvider(valobj, internal_dict):
0254     if valobj.IsValid():
0255         content = valobj.GetChildMemberWithName('(content)')
0256         if content.IsValid():
0257             summary = content.GetSummary()
0258             if summary is not None:
0259                 # unlike QString, we quoted the (content) twice to preserve our own quotation,
0260                 # must undo the quotation done by GetSummary
0261                 return 'b' + unquote(summary)
0262         # Something wrong with our synthetic provider, get the content by ourselves
0263         printable, _, _ = printableQByteArray(valobj)
0264         if printable is not None:
0265             # first replace " to \", and surround by "", no need to escape other things which
0266             # are handled in printableQByteArray.
0267             return 'b"{}"'.format(printable.replace('"', '\\"'))
0268     return '<Invalid>'
0269 
0270 
0271 class QByteArrayFormatter(HiddenMemberProvider):
0272     """A lldb synthetic provider for QByteArray"""
0273 
0274     def __init__(self, valobj, internal_dict):
0275         super(QByteArrayFormatter, self).__init__(valobj, internal_dict)
0276         self._char_type = valobj.GetType().GetBasicType(lldb.eBasicTypeChar)
0277         self._char_size = self._char_type.GetByteSize()
0278 
0279     def _update(self):
0280         printable, dataPointer, byteLength = printableQByteArray(self.valobj)
0281         self._num_children = byteLength
0282 
0283         if printable is not None:
0284             for idx in range(0, self._num_children):
0285                 var = self.valobj.CreateValueFromAddress('[{}]'.format(idx),
0286                                                          dataPointer + idx * self._char_size,
0287                                                          self._char_type)
0288                 self._addChild(var)
0289 
0290             # first replace " to \", and surround by "", no need to escape other things which
0291             # are handled in printableQByteArray.
0292             printable = b'"{}"'.format(printable.replace(b'"', b'\\"'))
0293             self._addChild(('(content)', printable), hidden=True)
0294 
0295 
0296 class BasicListFormatter(HiddenMemberProvider):
0297     """A lldb synthetic provider for QList like types"""
0298 
0299     def __init__(self, valobj, internal_dict, item_typename):
0300         super(BasicListFormatter, self).__init__(valobj, internal_dict)
0301         if item_typename is None:
0302             self._item_type = valobj.GetType().GetTemplateArgumentType(0)
0303         else:
0304             self._item_type = valobj.GetTarget().FindFirstType(item_typename)
0305         pvoid_type = valobj.GetTarget().GetBasicType(lldb.eBasicTypeVoid).GetPointerType()
0306         self._pvoid_size = pvoid_type.GetByteSize()
0307 
0308         # from QTypeInfo::isLarge
0309         isLarge = self._item_type.GetByteSize() > self._pvoid_size
0310 
0311         # unfortunately we can't use QTypeInfo<T>::isStatic as it's all inlined, so use
0312         # this list of types that use Q_DECLARE_TYPEINFO(T, Q_MOVABLE_TYPE)
0313         # (obviously it won't work for custom types)
0314         movableTypes = ['QRect', 'QRectF', 'QString', 'QMargins', 'QLocale', 'QChar', 'QDate',
0315                         'QTime', 'QDateTime', 'QVector', 'QRegExpr', 'QPoint', 'QPointF', 'QByteArray',
0316                         'QSize', 'QSizeF', 'QBitArray', 'QLine', 'QLineF', 'QModelIndex',
0317                         'QPersitentModelIndex', 'QVariant', 'QFileInfo', 'QUrl', 'QXmlStreamAttribute',
0318                         'QXmlStreamNamespaceDeclaration', 'QXmlStreamNotationDeclaration',
0319                         'QXmlStreamEntityDeclaration', 'QPair<int, int>']
0320         movableTypes = [valobj.GetTarget().FindFirstType(t) for t in movableTypes]
0321         # this list of types that use Q_DECLARE_TYPEINFO(T, Q_PRIMITIVE_TYPE) (from qglobal.h)
0322         primitiveTypes = ['bool', 'char', 'signed char', 'unsigned char', 'short', 'unsigned short',
0323                           'int', 'unsigned int', 'long', 'unsigned long', 'long long',
0324                           'unsigned long long', 'float', 'double']
0325         primitiveTypes = [valobj.GetTarget().FindFirstType(t) for t in primitiveTypes]
0326 
0327         if self._item_type in movableTypes or self._item_type in primitiveTypes:
0328             isStatic = False
0329         else:
0330             isStatic = not self._item_type.IsPointerType()
0331 
0332         # see QList::Node::t()
0333         self._externalStorage = isLarge or isStatic
0334         # If is external storage, then the node (a void*) is a pointer to item
0335         # else the item is stored inside the node
0336         if self._externalStorage:
0337             self._node_type = self._item_type.GetPointerType()
0338         else:
0339             self._node_type = self._item_type
0340 
0341     def _update(self):
0342         d = self.valobj.GetChildMemberWithName('d')
0343         begin = d.GetChildMemberWithName('begin').GetValueAsSigned(-1)
0344         end = d.GetChildMemberWithName('end').GetValueAsSigned(-1)
0345         array = d.GetChildMemberWithName('array')
0346 
0347         # sanity check
0348         if begin < 0 or end < 0 or end < begin:
0349             return
0350 
0351         self._num_children = end - begin
0352 
0353         for idx in range(0, self._num_children):
0354             offset = (begin + idx) * self._pvoid_size
0355             name = '[{}]'.format(idx)
0356             var = array.CreateChildAtOffset(name, offset, self._node_type)
0357             if self._externalStorage:
0358                 # can't use var.Dereference() directly, as the returned SBValue has '*' prepended
0359                 # to its name. And SBValue name can't be changed once constructed.
0360                 var = self.valobj.CreateValueFromData(name, var.GetPointeeData(),
0361                                                       self._item_type)
0362             self._addChild(var)
0363 
0364 
0365 class QListFormatter(BasicListFormatter):
0366     """lldb synthetic provider for QList"""
0367 
0368     def __init__(self, valobj, internal_dict):
0369         super(QListFormatter, self).__init__(valobj, internal_dict, None)
0370 
0371 
0372 class QStringListFormatter(BasicListFormatter):
0373     """lldb synthetic provider for QStringList"""
0374 
0375     def __init__(self, valobj, internal_dict):
0376         super(QStringListFormatter, self).__init__(valobj, internal_dict, 'QString')
0377 
0378 
0379 class QQueueFormatter(BasicListFormatter):
0380     """lldb synthetic provider for QQueue"""
0381 
0382     def __init__(self, valobj, internal_dict):
0383         super(QQueueFormatter, self).__init__(valobj.GetChildAtIndex(0), internal_dict, None)
0384         self.actualobj = valobj
0385 
0386     def update(self):
0387         self.valobj = self.actualobj.GetChildAtIndex(0)
0388         super(QQueueFormatter, self).update()
0389 
0390 
0391 class BasicVectorFormatter(HiddenMemberProvider):
0392     """A lldb synthetic provider for QVector like types"""
0393 
0394     def __init__(self, valobj, internal_dict):
0395         super(BasicVectorFormatter, self).__init__(valobj, internal_dict)
0396         self._item_type = valobj.GetType().GetTemplateArgumentType(0)
0397         self._item_size = self._item_type.GetByteSize()
0398 
0399     def _update(self):
0400         d = self.valobj.GetChildMemberWithName('p')
0401         # Qt4 has 'p', Qt5 doesn't
0402         isQt4 = d.IsValid()
0403         if isQt4:
0404             pArray = d.GetChildMemberWithName('array').AddressOf().GetValueAsUnsigned(0)
0405         else:
0406             d = self.valobj.GetChildMemberWithName('d')
0407             offset = d.GetChildMemberWithName('offset')
0408             pArray = d.GetValueAsUnsigned(0) + offset.GetValueAsUnsigned(0)
0409 
0410         # sanity check
0411         if not toSBPointer(self.valobj, pArray, self._item_type).IsValid():
0412             return
0413 
0414         # self._num_children = d.GetChildMemberWithName('size').GetValueAsUnsigned(0)
0415         self._num_children = d.GetChildMemberWithName('size').GetValueAsSigned(-1)
0416         if self._num_children < 0:
0417             return
0418 
0419         if self._num_children > self._capping_size():
0420             self._num_children = self._capping_size()
0421 
0422         for idx in range(0, self._num_children):
0423             var = self.valobj.CreateValueFromAddress('[{}]'.format(idx),
0424                                                      pArray + idx * self._item_size,
0425                                                      self._item_type)
0426             self._addChild(var)
0427 
0428 
0429 class QVectorFormatter(BasicVectorFormatter):
0430     """lldb synthetic provider for QVector"""
0431     def __init__(self, valobj, internal_dict):
0432         super(QVectorFormatter, self).__init__(valobj, internal_dict)
0433 
0434 
0435 class QStackFormatter(BasicVectorFormatter):
0436     """lldb synthetic provider for QStack"""
0437 
0438     def __init__(self, valobj, internal_dict):
0439         super(QStackFormatter, self).__init__(valobj.GetChildAtIndex(0), internal_dict)
0440         self.actualobj = valobj
0441 
0442     def update(self):
0443         self.valobj = self.actualobj.GetChildAtIndex(0)
0444         super(QStackFormatter, self).update()
0445 
0446 
0447 class QLinkedListFormatter(HiddenMemberProvider):
0448     """A lldb synthetic provider for QLinkedList"""
0449 
0450     def __init__(self, valobj, internal_dict):
0451         super(QLinkedListFormatter, self).__init__(valobj, internal_dict)
0452         self._item_type = valobj.GetType().GetTemplateArgumentType(0)
0453 
0454     def _update(self):
0455         d = self.valobj.GetChildMemberWithName('d')
0456         self._num_children = d.GetChildMemberWithName('size').GetValueAsSigned(-1)
0457 
0458         if self._num_children < 0:
0459             return
0460 
0461         node = self.valobj.GetChildMemberWithName('e').GetChildMemberWithName('n')
0462 
0463         for idx in range(0, self._num_children):
0464             if not node.IsValid():
0465                 self._members = []
0466                 self._num_children = 0
0467                 return
0468             var = node.GetChildMemberWithName('t')
0469             node = node.GetChildMemberWithName('n')
0470 
0471             var = self.valobj.CreateValueFromData('[{}]'.format(idx),
0472                                                   var.GetData(),
0473                                                   self._item_type)
0474             self._addChild(var)
0475 
0476 
0477 class KeyValueFormatter(object):
0478     """A lldb synthetic provider for (key,value) pair like types"""
0479 
0480     def __init__(self, valobj, internal_dict):
0481         self.valobj = valobj
0482         self._key_item = None
0483         self._val_item = None
0484 
0485     def num_children(self):
0486         return 2
0487 
0488     def has_children(self):
0489         return True
0490 
0491     def get_child_index(self, name):
0492         if name == 'key':
0493             return 0
0494         elif name == 'value':
0495             return 1
0496         return None
0497 
0498     def get_child_at_index(self, idx):
0499         if idx < 0 or idx >= 2:
0500             return None
0501         if idx == 0:
0502             return self._key_item
0503         elif idx == 1:
0504             return self._val_item
0505         return None
0506 
0507     def update(self):
0508         if not self.valobj.IsValid():
0509             return
0510         self._key_item = self.valobj.GetChildMemberWithName('key')
0511         self._val_item = self.valobj.GetChildMemberWithName('value')
0512 
0513 
0514 def KeyValueSummaryProvider(valobj, internal_dict):
0515     if not valobj.IsValid():
0516         return None
0517 
0518     key = valobj.GetChildMemberWithName('key')
0519     value = valobj.GetChildMemberWithName('value')
0520     key_summary = key.GetSummary() or key.GetValue()  # show value if summary is empty or None
0521     val_summary = value.GetSummary() or value.GetValue()  # show value if summary is empty or None
0522     return '({}, {})'.format(key_summary, val_summary)
0523 
0524 
0525 class BasicMapFormatter(HiddenMemberProvider):
0526     """A lldb synthetic provider for QMap like types"""
0527 
0528     def __init__(self, valobj, internal_dict):
0529         super(BasicMapFormatter, self).__init__(valobj, internal_dict)
0530         self_type = valobj.GetType()
0531         key_type = self_type.GetTemplateArgumentType(0)
0532         val_type = self_type.GetTemplateArgumentType(1)
0533         # the ' ' between two template arguments is significant,
0534         # otherwise FindFirstType returns None
0535         node_typename = 'QMapNode<{}, {}>'.format(key_type.GetName(), val_type.GetName())
0536         node_typename = canonicalized_type_name(node_typename)
0537         self._node_type = valobj.GetTarget().FindFirstType(node_typename)
0538 
0539         e = self.valobj.GetChildMemberWithName('e')
0540         self.isQt4 = e.IsValid()
0541         if self.isQt4:
0542             self._payload_size = self._qt4_calc_payload(key_type, val_type)
0543 
0544     def _qt4_calc_payload(self, key_type, val_type):
0545         """calculate payload size for Qt4"""
0546         str = lldb.SBStream()
0547         self.valobj.GetExpressionPath(str, True)
0548         expr = '{}.payload()'.format(str.GetData())
0549         ret = lldb.frame.EvaluateExpression(expr).GetValueAsUnsigned(0)
0550         if ret != 0:
0551             return ret
0552         else:
0553             # if the inferior function call didn't work, let's try to calculate ourselves
0554             target = self.valobj.GetTarget()
0555             pvoid_type = target.GetBasicType(lldb.eBasicTypeVoid).GetPointerType()
0556             pvoid_size = pvoid_type.GetByteSize()
0557 
0558             # we can't use QMapPayloadNode as it's inlined
0559             # as a workaround take the sum of sizeof(members)
0560             ret = key_type.GetByteSize()
0561             ret += val_type.GetByteSize()
0562             ret += pvoid_size
0563 
0564             # but because of data alignment the value can be higher
0565             # so guess it's aliged by sizeof(void*)
0566             # TODO: find a real solution for this problem
0567             ret += ret % pvoid_size
0568 
0569             # for some reason booleans are different
0570             if val_type == target.GetBasicType(lldb.eBasicTypeBool):
0571                 ret += 2
0572 
0573             ret -= pvoid_size
0574             return ret
0575 
0576     class _iteratorQt4(Iterator):
0577         """Map iterator for Qt4"""
0578 
0579         def __init__(self, headerobj, node_type, payload_size):
0580             self.current = headerobj.GetChildMemberWithName('forward').GetChildAtIndex(0)
0581             self.header_addr = headerobj.GetValueAsUnsigned(0)
0582             self.node_type = node_type
0583             self.payload_size = payload_size
0584 
0585             # sanity check
0586             self.is_garbage = False
0587             if not validAddr(headerobj, self.header_addr):
0588                 self.is_garbage = True
0589             if not validPointer(self.current):
0590                 self.is_garbage = True
0591 
0592         def __iter__(self):
0593             return self
0594 
0595         def concrete(self, pdata_node):
0596             pnode_addr = pdata_node.GetValueAsUnsigned(0)
0597             pnode_addr -= self.payload_size
0598 
0599             return toSBPointer(self.current, pnode_addr, self.node_type)
0600 
0601         def __next__(self):
0602             if self.is_garbage:
0603                 raise StopIteration
0604             if self.current.GetValueAsUnsigned(0) == self.header_addr:
0605                 raise StopIteration
0606             pnode = self.concrete(self.current)
0607             self.current = self.current.GetChildMemberWithName('forward').GetChildAtIndex(0)
0608             return pnode
0609 
0610     class _iteratorQt5(Iterator):
0611         """Map iterator for Qt5"""
0612 
0613         def __init__(self, dataobj, pnode_type):
0614             self.pnode_type = pnode_type
0615             self.root = dataobj.GetChildMemberWithName('header')
0616             self.current = lldb.SBValue()
0617 
0618             # We store the path here to avoid keeping re-fetching
0619             # values from the inferior (also, skip the pointer
0620             # arithmetic involved in using the parent pointer
0621             self.path = []
0622 
0623         def __iter__(self):
0624             return self
0625 
0626         def moveToNextNode(self):
0627             def isNullPointer(val):
0628                 return not val.IsValid() or val.GetValueAsUnsigned(0) == 0
0629 
0630             if isNullPointer(self.current):
0631                 # find the leftmost node
0632                 left = self.root.GetChildMemberWithName('left')
0633                 if isNullPointer(left):
0634                     return False
0635                 self.current = self.root
0636                 while not isNullPointer(left):
0637                     self.path.append(self.current)
0638                     self.current = left
0639                     left = self.current.GetChildMemberWithName('left')
0640             else:
0641                 right = self.current.GetChildMemberWithName('right')
0642                 if not isNullPointer(right):
0643                     self.path.append(self.current)
0644                     self.current = right
0645                     left = self.current.GetChildMemberWithName('left')
0646                     while not isNullPointer(left):
0647                         self.path.append(self.current)
0648                         self.current = left
0649                         left = self.current.GetChildMemberWithName('left')
0650                 else:
0651                     last = self.current
0652                     self.current = self.path.pop()
0653                     right = self.current.GetChildMemberWithName('right')
0654                     while right.GetValueAsUnsigned(0) == last.GetValueAsUnsigned(0):
0655                         last = self.current
0656                         self.current = self.path.pop()
0657                         right = self.current.GetChildMemberWithName('right')
0658                     # if there are no more parents, we are at the root
0659                     if len(self.path) == 0:
0660                         return False
0661             return True
0662 
0663         def __next__(self):
0664             if not self.moveToNextNode():
0665                 raise StopIteration
0666             return self.current.Cast(self.pnode_type)
0667 
0668     def _update(self):
0669         pnode_type = self._node_type.GetPointerType()
0670         if self.isQt4:
0671             e = self.valobj.GetChildMemberWithName('e')
0672             it = self._iteratorQt4(e, self._node_type, self._payload_size)
0673         else:
0674             d = self.valobj.GetChildMemberWithName('d')
0675             it = self._iteratorQt5(d, pnode_type)
0676 
0677         self._num_children = 0
0678         for pnode in it:
0679             # dereference node and change to a user friendly name
0680             name = '[{}]'.format(self._num_children)
0681             self._num_children += 1
0682             var = self.valobj.CreateValueFromData(name, pnode.GetPointeeData(),
0683                                                   self._node_type)
0684             self._addChild(var)
0685 
0686 
0687 class QMapFormatter(BasicMapFormatter):
0688     """lldb synthetic provider for QMap"""
0689 
0690     def __init__(self, valobj, internal_dict):
0691         super(QMapFormatter, self).__init__(valobj, internal_dict)
0692 
0693 
0694 class QMultiMapFormatter(BasicMapFormatter):
0695     """lldb synthetic provider for QMap"""
0696 
0697     def __init__(self, valobj, internal_dict):
0698         super(QMultiMapFormatter, self).__init__(valobj.GetChildAtIndex(0), internal_dict)
0699         self.actualobj = valobj
0700 
0701     def update(self):
0702         self.valobj = self.actualobj.GetChildAtIndex(0)
0703         super(QMultiMapFormatter, self).update()
0704 
0705 
0706 class BasicHashFormatter(HiddenMemberProvider):
0707     """A lldb synthetic provider for QHash like types"""
0708 
0709     def __init__(self, valobj, internal_dict):
0710         super(BasicHashFormatter, self).__init__(valobj, internal_dict)
0711         self_type = valobj.GetType()
0712         self._key_type = self_type.GetTemplateArgumentType(0)
0713         self._val_type = self_type.GetTemplateArgumentType(1)
0714         node_typename = 'QHashNode<{}, {}>'.format(self._key_type.GetName(),
0715                                                    self._val_type.GetName())
0716         node_typename = canonicalized_type_name(node_typename)
0717 
0718         self._node_type = valobj.GetTarget().FindFirstType(node_typename)
0719 
0720     class _iterator(Iterator):
0721         """Hash iterator"""
0722         def __init__(self, valobj, pnode_type):
0723             d = valobj.GetChildMemberWithName('d')
0724             self.buckets = d.GetChildMemberWithName('buckets')
0725             self.null_node = valobj.GetChildMemberWithName('e')
0726             self.pnode_type = pnode_type
0727 
0728             self.num_buckets = d.GetChildMemberWithName('numBuckets').GetValueAsSigned(-1)
0729 
0730             self.is_garbage = False
0731             if self.num_buckets < -1:
0732                 self.is_garbage = True
0733                 return
0734             self.current = self.firstNode()
0735 
0736         def __iter__(self):
0737             return self
0738 
0739         def findNode(self, start=0):
0740             """Iterate through buckets, start at `start`,
0741                return any bucket the is not the null_node, or the null_node itself if nothing found.
0742                adapted from QHashData::firstNode
0743             """
0744             null_node_addr = self.null_node.GetValueAsUnsigned(0)
0745             for idx in range(start, self.num_buckets):
0746                 # self.buckets has type QHashData::Node**, not an array
0747                 # calling GetChildAtIndex with use_synthetic=True so the pointer is used as an array
0748                 bucket = self.buckets.GetChildAtIndex(idx, lldb.eDynamicCanRunTarget, True)
0749                 if bucket.GetValueAsUnsigned(0) != null_node_addr:
0750                     # in Qt4, QHashData::Node is incomplete type, but QHashNode is complete,
0751                     # so always use QHashNode
0752                     return bucket.Cast(self.pnode_type)
0753             return self.null_node
0754 
0755         def firstNode(self):
0756             return self.findNode()
0757 
0758         def moveToNextNode(self):
0759             """Get the nextNode after the current, see also QHashData::nextNode()."""
0760             next = self.current.GetChildMemberWithName('next')
0761 
0762             if next.GetValueAsUnsigned(0) != self.null_node.GetValueAsUnsigned(0):
0763                 self.current = next
0764             else:
0765                 h = self.current.GetChildMemberWithName('h').GetValueAsUnsigned(0)
0766                 start = (h % self.num_buckets) + 1
0767                 self.current = self.findNode(start)
0768 
0769         def __next__(self):
0770             if self.is_garbage:
0771                 raise StopIteration
0772             if self.current.GetValueAsUnsigned(0) == self.null_node.GetValueAsUnsigned(0):
0773                 raise StopIteration
0774             pnode = self.current
0775             self.moveToNextNode()
0776             return pnode
0777 
0778     def _update(self):
0779         self._num_children = self.valobj.GetChildMemberWithName('d').GetChildMemberWithName('size').GetValueAsSigned(-1)
0780         if self._num_children < 0:
0781             return
0782 
0783         idx = 0
0784         for pnode in self._iterator(self.valobj, self._node_type.GetPointerType()):
0785             if idx >= self._num_children:
0786                 self._members = []
0787                 self._num_children = 0
0788                 break
0789             # dereference node and change to a user friendly name
0790             name = '[{}]'.format(idx)
0791             idx += 1
0792             var = self.valobj.CreateValueFromData(name, pnode.GetPointeeData(),
0793                                                   self._node_type)
0794             self._addChild(var)
0795         if idx != self._num_children:
0796             self._members = []
0797             self._num_children = 0
0798 
0799 
0800 class QHashFormatter(BasicHashFormatter):
0801     """lldb synthetic provider for QHash"""
0802 
0803     def __init__(self, valobj, internal_dict):
0804         super(QHashFormatter, self).__init__(valobj, internal_dict)
0805 
0806 
0807 class QMultiHashFormatter(BasicHashFormatter):
0808     """lldb synthetic provider for QHash"""
0809 
0810     def __init__(self, valobj, internal_dict):
0811         super(QMultiHashFormatter, self).__init__(valobj.GetChildAtIndex(0), internal_dict)
0812         self.actualobj = valobj
0813 
0814     def update(self):
0815         self.valobj = self.actualobj.GetChildAtIndex(0)
0816         super(QMultiHashFormatter, self).update()
0817 
0818 
0819 class QSetFormatter(HiddenMemberProvider):
0820     """lldb synthetic provider for QSet"""
0821 
0822     def __init__(self, valobj, internal_dict):
0823         super(QSetFormatter, self).__init__(valobj, internal_dict)
0824         self._hash_formatter = QHashFormatter(valobj.GetChildMemberWithName('q_hash'),
0825                                               internal_dict)
0826 
0827     def num_children(self):
0828         return self._num_children
0829 
0830     def _update(self):
0831         self._hash_formatter.valobj = self.valobj.GetChildMemberWithName('q_hash')
0832         self._hash_formatter.update()
0833 
0834         self._num_children = 0
0835         for node in self._hash_formatter._members:
0836             keydata = node.GetChildMemberWithName('key').GetData()
0837             name = '[{}]'.format(self._num_children)
0838             var = self.valobj.CreateValueFromData(name, keydata, self._hash_formatter._key_type)
0839             self._addChild(var)
0840             self._num_children += 1
0841 
0842 
0843 class QDateFormatter(HiddenMemberProvider):
0844     """lldb synthetic provider for QDate"""
0845     def __init__(self, valobj, internal_dict):
0846         super(QDateFormatter, self).__init__(valobj, internal_dict)
0847         self._add_original = False
0848         self._qstring_type = valobj.GetTarget().FindFirstType('QString')
0849 
0850     def has_children(self):
0851         return True
0852 
0853     @staticmethod
0854     def parse(julianDay):
0855         """Copied from Qt srources"""
0856         if julianDay == 0:
0857             return None
0858         if julianDay >= 2299161:
0859             # Gregorian calendar starting from October 15, 1582
0860             # This algorithm is from Henry F. Fliegel and Thomas C. Van Flandern
0861             ell = julianDay + 68569
0862             n = (4 * ell) / 146097
0863             ell = ell - (146097 * n + 3) / 4
0864             i = (4000 * (ell + 1)) / 1461001
0865             ell = ell - (1461 * i) / 4 + 31
0866             j = (80 * ell) / 2447
0867             d = ell - (2447 * j) / 80
0868             ell = j / 11
0869             m = j + 2 - (12 * ell)
0870             y = 100 * (n - 49) + i + ell
0871         else:
0872             # Julian calendar until October 4, 1582
0873             # Algorithm from Frequently Asked Questions about Calendars by Claus Toendering
0874             julianDay += 32082
0875             dd = (4 * julianDay + 3) / 1461
0876             ee = julianDay - (1461 * dd) / 4
0877             mm = ((5 * ee) + 2) / 153
0878             d = ee - (153 * mm + 2) / 5 + 1
0879             m = mm + 3 - 12 * (mm / 10)
0880             y = dd - 4800 + (mm / 10)
0881             if y <= 0:
0882                 return None
0883         return dt.date(y, m, d)
0884 
0885     def _update(self):
0886         # FIXME: Calling functions returns incorrect SBValue for complex type in lldb
0887         # # toString
0888         # res = invoke(self.valobj, 'toString', '0')
0889         # self._addChild(rename('toString', res))
0890 
0891         # jd
0892         julianDay = self.valobj.GetChildMemberWithName('jd')
0893         self._addChild(julianDay)
0894 
0895         pydate = self.parse(julianDay.GetValueAsUnsigned(0))
0896         if pydate is None:
0897             return
0898         # (ISO)
0899         iso_str = pydate.isoformat().decode().__repr__()[2:-1]
0900         self._addChild(('(ISO)', iso_str))
0901 
0902         # (Locale)
0903         locale_encoding = [locale.getlocale()[1]]
0904         if locale_encoding[0] is None:
0905             locale_encoding = []
0906         locale_str = pydate.strftime('%x').decode(*locale_encoding).__repr__()[2:-1]
0907         self._addChild(('(Locale)', locale_str))
0908 
0909 
0910 def QDateSummaryProvider(valobj, internal_dict):
0911     if valobj.IsValid():
0912         content = valobj.GetChildMemberWithName('(Locale)')
0913         if content.IsValid():
0914             summary = content.GetSummary()
0915             if summary is not None:
0916                 return summary
0917         # No synthetic provider installed, get the content by ourselves
0918         pydate = QDateFormatter.parse(valobj.GetChildMemberWithName('jd').GetValueAsUnsigned(0))
0919         if pydate is not None:
0920             return pydate.isoformat().decode().__repr__()[2:-1]
0921     return '<Invalid>'
0922 
0923 
0924 class QTimeFormatter(HiddenMemberProvider):
0925     """lldb synthetic provider for QTime"""
0926     def __init__(self, valobj, internal_dict):
0927         super(QTimeFormatter, self).__init__(valobj, internal_dict)
0928         self._add_original = False
0929 
0930     def has_children(self):
0931         return True
0932 
0933     @staticmethod
0934     def parse(ds):
0935         if ds < 0:
0936             return None
0937         MSECS_PER_HOUR = 3600000
0938         SECS_PER_MIN = 60
0939         MSECS_PER_MIN = 60000
0940 
0941         hour = ds / MSECS_PER_HOUR
0942         minute = (ds % MSECS_PER_HOUR) / MSECS_PER_MIN
0943         second = (ds / 1000) % SECS_PER_MIN
0944         msec = ds % 1000
0945         return dt.time(hour, minute, second, msec)
0946 
0947     def _update(self):
0948         # FIXME: Calling functions returns incorrect SBValue for complex type in lldb
0949         # # toString
0950         # res = invoke(self.valobj, 'toString', '0')
0951         # self._addChild(rename('toString', res))
0952 
0953         # mds
0954         mds = self.valobj.GetChildMemberWithName('mds')
0955         self._addChild(mds)
0956 
0957         pytime = self.parse(mds.GetValueAsUnsigned(0))
0958         if pytime is None:
0959             return
0960         # (ISO)
0961         iso_str = pytime.isoformat().decode().__repr__()[2:-1]
0962         self._addChild(('(ISO)', iso_str))
0963 
0964         # (Locale)
0965         locale_encoding = [locale.getlocale()[1]]
0966         if locale_encoding[0] is None:
0967             locale_encoding = []
0968         locale_str = pytime.strftime('%X').decode(*locale_encoding).__repr__()[2:-1]
0969         self._addChild(('(Locale)', locale_str))
0970 
0971 
0972 def QTimeSummaryProvider(valobj, internal_dict):
0973     if valobj.IsValid():
0974         content = valobj.GetChildMemberWithName('(Locale)')
0975         if content.IsValid():
0976             summary = content.GetSummary()
0977             if summary is not None:
0978                 return summary
0979         # No synthetic provider installed, get the content by ourselves
0980         pytime = QTimeFormatter.parse(valobj.GetChildMemberWithName('mds').GetValueAsUnsigned(0))
0981         if pytime is not None:
0982             return pytime.isoformat().decode().__repr__()[2:-1]
0983     return None
0984 
0985 
0986 class QDateTimeFormatter(HiddenMemberProvider):
0987     """lldb synthetic provider for QTime"""
0988     def __init__(self, valobj, internal_dict):
0989         super(QDateTimeFormatter, self).__init__(valobj, internal_dict)
0990 
0991     def has_children(self):
0992         return True
0993 
0994     @staticmethod
0995     def parse(time_t, utc=False):
0996         if time_t is None:
0997             return None
0998         totuple = time.gmtime if utc else time.localtime
0999         return totuple(time_t)
1000 
1001     @staticmethod
1002     def getdata(var):
1003         # FIXME: data member is in private structure, which has no complete type when no debug info
1004         # available for Qt.So we can only rely on function call.
1005         # The comments in Qt source code says data member will be inlined in Qt6,
1006         res = invoke(var, 'toSecsSinceEpoch')
1007         return res
1008 
1009     def _update(self):
1010         time_t = self.getdata(self.valobj)
1011         if not time_t.IsValid():
1012             return
1013 
1014         locale_encoding = [locale.getlocale()[1]]
1015         if locale_encoding[0] is None:
1016             locale_encoding = []
1017 
1018         # toTime_t
1019         self._addChild(rename('toTime_t', time_t))
1020 
1021         # time tuple in local time and utc time
1022         local_tt = self.parse(time_t.GetValueAsUnsigned(0))
1023         utc_tt = self.parse(time_t.GetValueAsUnsigned(0), utc=True)
1024 
1025         # (ISO)
1026         formatted = time.strftime('%Y-%m-%d %H:%M:%S', utc_tt).decode(*locale_encoding).__repr__()
1027         formatted = formatted[2:-1]
1028         self._addChild(('(ISO)', formatted))
1029 
1030         def locale_fmt(name, tt):
1031             formatted = time.strftime('%c', tt).decode(*locale_encoding).__repr__()[2:-1]
1032             self._addChild((name, formatted))
1033 
1034         # (Locale)
1035         locale_fmt('(Locale)', local_tt)
1036 
1037         # (UTC)
1038         locale_fmt('(UTC)', utc_tt)
1039 
1040         # FIXME: Calling functions returns incorrect SBValue for complex type in lldb
1041         # # toString
1042         # res = invoke(self.valobj, 'toString', '0')
1043         # print 'tostring', res
1044         # self._addChild(rename('toString', res))
1045 
1046         # # toLocalTime
1047         # res = invoke(self.valobj, 'toTimeSpec', '0')  # Qt::LocalTime == 0
1048         # print 'tolocaltime', res
1049         # self._addChild(rename('toLocalTime', res))
1050 
1051 
1052 def QDateTimeSummaryProvider(valobj, internal_dict):
1053     if valobj.IsValid():
1054         content = valobj.GetChildMemberWithName('(Locale)')
1055         if content.IsValid():
1056             summary = content.GetSummary()
1057             if summary is not None:
1058                 return summary
1059         # No synthetic provider installed, get the content by ourselves
1060         pytime = QDateTimeFormatter.parse(QDateTimeFormatter.getdata(valobj).GetValueAsUnsigned(0))
1061         if pytime is not None:
1062             formatted = time.strftime('%Y-%m-%d %H:%M:%S', pytime).decode().__repr__()
1063             formatted = formatted[2:-1]
1064             return formatted
1065     return None
1066 
1067 
1068 class QUrlFormatter(HiddenMemberProvider):
1069     """docstring for QUrlFormatter"""
1070     def __init__(self, valobj, internal_dict):
1071         super(QUrlFormatter, self).__init__(valobj, internal_dict)
1072 
1073         target = valobj.GetTarget()
1074         self._int_type = target.GetBasicType(lldb.eBasicTypeInt)
1075         self._pvoid_type = target.GetBasicType(lldb.eBasicTypeVoid).GetPointerType()
1076         self._qstring_type = target.FindFirstType('QString')
1077         self._qbytearray_type = target.FindFirstType('QByteArray')
1078 
1079     def parseQt5Data(self, dataobj):
1080         def constructEncoded(port, scheme, username, password, host, path, query, fragment):
1081             netloc = ''
1082             host_str = printableQString(host)[0]
1083             if host_str is not None:
1084                 username_str = printableQString(username)[0]
1085                 if username_str is not None:
1086                     netloc += username_str
1087                     password_str = printableQString(password)[0]
1088                     if password_str is not None:
1089                         netloc += ':' + password_str
1090                     netloc += "@"
1091                 netloc += host_str
1092                 port_num = port.GetValueAsSigned(-1)
1093                 if port_num != -1:
1094                     netloc += ":" + str(port_num)
1095 
1096             url = urlunsplit((printableQString(scheme)[0],
1097                               netloc,
1098                               printableQString(path)[0],
1099                               printableQString(query)[0],
1100                               printableQString(fragment)[0]))
1101             encoded = None
1102             if len(url) > 0:
1103                 encoded = ('(encoded)', url)
1104             return (encoded, port, scheme, username, password, host, path, query, fragment)
1105 
1106         # try if there's debug info available
1107         port = dataobj.GetChildMemberWithName('port')
1108         if port.IsValid():
1109             scheme = dataobj.GetChildMemberWithName('scheme')
1110             username = dataobj.GetChildMemberWithName('userName')
1111             password = dataobj.GetChildMemberWithName('password')
1112             host = dataobj.GetChildMemberWithName('host')
1113             path = dataobj.GetChildMemberWithName('path')
1114             query = dataobj.GetChildMemberWithName('query')
1115             fragment = dataobj.GetChildMemberWithName('fragment')
1116             return constructEncoded(port, scheme, username, password, host, path, query, fragment)
1117         # if no debug information is available for Qt, try guessing the correct address
1118         # problem with this is that if QUrlPrivate members get changed, this fails
1119         addr = dataobj.GetValueAsUnsigned(0)
1120 
1121         # skip QAtomicInt ref
1122         addr += self._int_type.GetByteSize()
1123         # handle int port
1124         port = dataobj.CreateValueFromAddress('(port)', addr, self._int_type)
1125         addr += self._int_type.GetByteSize()
1126         # handle QString scheme
1127         scheme = dataobj.CreateValueFromAddress('(scheme)', addr, self._qstring_type)
1128         addr += self._qstring_type.GetByteSize()
1129         # handle QString username
1130         username = dataobj.CreateValueFromAddress('(userName)', addr, self._qstring_type)
1131         addr += self._qstring_type.GetByteSize()
1132         # handle QString password
1133         password = dataobj.CreateValueFromAddress('(password)', addr, self._qstring_type)
1134         addr += self._qstring_type.GetByteSize()
1135         # handle QString host
1136         host = dataobj.CreateValueFromAddress('(host)', addr, self._qstring_type)
1137         addr += self._qstring_type.GetByteSize()
1138         # handle QString path
1139         path = dataobj.CreateValueFromAddress('(path)', addr, self._qstring_type)
1140         addr += self._qstring_type.GetByteSize()
1141         # handle QString query
1142         query = dataobj.CreateValueFromAddress('(query)', addr, self._qstring_type)
1143         addr += self._qstring_type.GetByteSize()
1144         # handle QString fragment
1145         fragment = dataobj.CreateValueFromAddress('(fragment)', addr, self._qstring_type)
1146 
1147         return constructEncoded(port, scheme, username, password, host, path, query, fragment)
1148 
1149     def parseQt4Data(self, dataobj):
1150         def parseComponents(encodedobj):
1151             url, _, _ = printableQByteArray(encodedobj)
1152             if url is None:
1153                 return (None,) * 9
1154             res = urlsplit(url)
1155             port = dataobj.CreateValueFromExpression('(port)', str(res.port if res.port is not None else -1))
1156             scheme = ('(scheme)', res.scheme)
1157             username = ('(username)', res.username if res.username is not None else '')
1158             password = ('(password)', res.password if res.password is not None else '')
1159             host = ('(host)', res.hostname if res.hostname is not None else '')
1160             path = ('(path)', res.path)
1161             query = ('(query)', res.query)
1162             fragment = ('(fragment)', res.fragment)
1163             encoded = ('(encoded)', url)
1164             return (encoded, port, scheme, username, password, host, path, query, fragment)
1165 
1166         encodedOriginal = dataobj.GetChildMemberWithName('encodedOriginal')
1167         if encodedOriginal.IsValid():
1168             return parseComponents(encodedOriginal)
1169 
1170         # if no debug information is available for Qt, try guessing the correct address
1171         # problem with this is that if QUrlPrivate members get changed, this fails
1172         addr = dataobj.GetValueAsUnsigned(0)
1173         if not validAddr(dataobj, addr):
1174             return (None,) * 9
1175 
1176         # skip QAtomicInt ref
1177         addr += self._int_type.GetByteSize()
1178         # alignment,
1179         # The largest member is QString and QByteArray, which are 8 bytes (one sizeof(void*)),
1180         # int is aligned to 8 bytes
1181         addr += self._pvoid_type.GetByteSize() - self._int_type.GetByteSize()
1182         # These members are always empty: scheme, userName, password, host, path, query (QByteArray), fragment
1183         addr += self._qstring_type.GetByteSize() * 6
1184         addr += self._qbytearray_type.GetByteSize()
1185         # handle QByteArray encodedOriginal
1186         encoded = dataobj.CreateValueFromAddress('(encoded)', addr, self._qbytearray_type)
1187 
1188         if not encoded.IsValid():
1189             return (None,) * 9
1190         return parseComponents(encoded)
1191 
1192     def try_parse(self):
1193         dataobj = self.valobj.GetChildMemberWithName('d')
1194         # first try to access Qt4 data
1195         (encoded, port, scheme,
1196          username, password, host, path, query, fragment) = self.parseQt4Data(dataobj)
1197         if encoded is not None:
1198             return (encoded, port, scheme, username, password, host, path, query, fragment)
1199 
1200         # if this fails, maybe we deal with Qt5
1201         (encoded, port, scheme,
1202          username, password, host,
1203          path, query, fragment) = self.parseQt5Data(dataobj)
1204         if encoded is not None:
1205             return (encoded, port, scheme, username, password, host, path, query, fragment)
1206 
1207         # if above fails, try to print directly.
1208         # But this might not work, and could lead to issues
1209         # (see http://sourceware-org.1504.n7.nabble.com/help-Calling-malloc-from-a-Python-pretty-printer-td284031.html)
1210         res = invoke(self.valobj, 'toString', '(QUrl::FormattingOptions)0')  # QUrl::PrettyDecoded == 0
1211         if res.IsValid():
1212             return rename('(encoded)', res), None, None, None, None, None, None, None, None
1213         return None, None, None, None, None, None, None, None, None
1214 
1215     def _update(self):
1216         (encoded, port, scheme, username,
1217          password, host, path, query, fragment) = self.try_parse()
1218         if encoded is not None:
1219             self._addChild(encoded, hidden=True)
1220             if port is not None:
1221                 self._addChild(port)
1222                 self._addChild(scheme)
1223                 self._addChild(username)
1224                 self._addChild(password)
1225                 self._addChild(host)
1226                 self._addChild(path)
1227                 self._addChild(query)
1228                 self._addChild(fragment)
1229             return
1230         # if everything fails, we have no choice but to show the original member
1231         self._add_original = False
1232         self._addChild(self.valobj.GetChildMemberWithName('d'))
1233 
1234 
1235 def QUrlSummaryProvider(valobj, internal_dict):
1236     if valobj.IsValid():
1237         content = valobj.GetChildMemberWithName('(encoded)')
1238         if content.IsValid():
1239             summary = content.GetSummary()
1240             if summary is not None:
1241                 return summary
1242         # No synthetic provider installed, get the content by ourselves
1243         encoded = QUrlFormatter(valobj, internal_dict).try_parse()[0][1]
1244         if encoded is not None:
1245             return encoded
1246     return None
1247 
1248 
1249 class QUuidFormatter(HiddenMemberProvider):
1250     """A lldb synthetic provider for QUuid"""
1251     def __init__(self, valobj, internal_dict):
1252         super(QUuidFormatter, self).__init__(valobj, internal_dict)
1253 
1254     def has_children(self):
1255         return False
1256 
1257 
1258 def QUuidSummaryProvider(valobj, internal_dict):
1259     data = [valobj.GetChildMemberWithName(name).GetValueAsUnsigned(0)
1260             for name in ['data1', 'data2', 'data3']]
1261     data += [val.GetValueAsUnsigned(0) for val in valobj.GetChildMemberWithName('data4')]
1262 
1263     return 'QUuid({{{:02x}-{:02x}-{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}}})'.format(*data)