1############################################################################
2#
3# Copyright (C) 2016 The Qt Company Ltd.
4# Contact: https://www.qt.io/licensing/
5#
6# This file is part of Qt Creator.
7#
8# Commercial License Usage
9# Licensees holding valid commercial Qt licenses may use this file in
10# accordance with the commercial license agreement provided with the
11# Software or, alternatively, in accordance with the terms contained in
12# a written agreement between you and The Qt Company. For licensing terms
13# and conditions see https://www.qt.io/terms-conditions. For further
14# information use the contact form at https://www.qt.io/contact-us.
15#
16# GNU General Public License Usage
17# Alternatively, this file may be used under the terms of the GNU
18# General Public License version 3 as published by the Free Software
19# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20# included in the packaging of this file. Please review the following
21# information to ensure the GNU General Public License requirements will
22# be met: https://www.gnu.org/licenses/gpl-3.0.html.
23#
24############################################################################
25
26import os
27import codecs
28import collections
29import glob
30import struct
31import sys
32import base64
33import re
34import time
35import inspect
36from utils import DisplayFormat, TypeCode
37
38try:
39    # That's only used in native combined debugging right now, so
40    # we do not need to hard fail in cases of partial python installation
41    # that will never use this.
42    import json
43except:
44    print("Python module json not found. "
45          "Native combined debugging might not work.")
46    pass
47
48
49if sys.version_info[0] >= 3:
50    toInteger = int
51else:
52    toInteger = long
53
54
55class ReportItem():
56    """
57    Helper structure to keep temporary 'best' information about a value
58    or a type scheduled to be reported. This might get overridden be
59    subsequent better guesses during a putItem() run.
60    """
61
62    def __init__(self, value=None, encoding=None, priority=-100, elided=None):
63        self.value = value
64        self.priority = priority
65        self.encoding = encoding
66        self.elided = elided
67
68    def __str__(self):
69        return 'Item(value: %s, encoding: %s, priority: %s, elided: %s)' \
70            % (self.value, self.encoding, self.priority, self.elided)
71
72
73class Timer():
74    def __init__(self, d, desc):
75        self.d = d
76        self.desc = desc + '-' + d.currentIName
77
78    def __enter__(self):
79        self.starttime = time.time()
80
81    def __exit__(self, exType, exValue, exTraceBack):
82        elapsed = int(1000 * (time.time() - self.starttime))
83        self.d.timings.append([self.desc, elapsed])
84
85
86class Children():
87    def __init__(self, d, numChild=1, childType=None, childNumChild=None,
88                 maxNumChild=None, addrBase=None, addrStep=None):
89        self.d = d
90        self.numChild = numChild
91        self.childNumChild = childNumChild
92        self.maxNumChild = maxNumChild
93        if childType is None:
94            self.childType = None
95        else:
96            self.childType = childType.name
97            if not self.d.isCli:
98                self.d.putField('childtype', self.childType)
99            if childNumChild is not None:
100                self.d.putField('childnumchild', childNumChild)
101                self.childNumChild = childNumChild
102        if addrBase is not None and addrStep is not None:
103            self.d.put('addrbase="0x%x",addrstep="%d",' % (addrBase, addrStep))
104
105    def __enter__(self):
106        self.savedChildType = self.d.currentChildType
107        self.savedChildNumChild = self.d.currentChildNumChild
108        self.savedNumChild = self.d.currentNumChild
109        self.savedMaxNumChild = self.d.currentMaxNumChild
110        self.d.currentChildType = self.childType
111        self.d.currentChildNumChild = self.childNumChild
112        self.d.currentNumChild = self.numChild
113        self.d.currentMaxNumChild = self.maxNumChild
114        self.d.put(self.d.childrenPrefix)
115
116    def __exit__(self, exType, exValue, exTraceBack):
117        if exType is not None:
118            if self.d.passExceptions:
119                self.d.showException('CHILDREN', exType, exValue, exTraceBack)
120            self.d.putSpecialValue('notaccessible')
121            self.d.putNumChild(0)
122        if self.d.currentMaxNumChild is not None:
123            if self.d.currentMaxNumChild < self.d.currentNumChild:
124                self.d.put('{name="<incomplete>",value="",type="",numchild="0"},')
125        self.d.currentChildType = self.savedChildType
126        self.d.currentChildNumChild = self.savedChildNumChild
127        self.d.currentNumChild = self.savedNumChild
128        self.d.currentMaxNumChild = self.savedMaxNumChild
129        if self.d.isCli:
130            self.d.output += '\n' + '   ' * self.d.indent
131        self.d.put(self.d.childrenSuffix)
132        return True
133
134
135class SubItem():
136    def __init__(self, d, component):
137        self.d = d
138        self.name = component
139        self.iname = None
140
141    def __enter__(self):
142        self.d.enterSubItem(self)
143
144    def __exit__(self, exType, exValue, exTraceBack):
145        return self.d.exitSubItem(self, exType, exValue, exTraceBack)
146
147
148class TopLevelItem(SubItem):
149    def __init__(self, d, iname):
150        self.d = d
151        self.iname = iname
152        self.name = None
153
154
155class UnnamedSubItem(SubItem):
156    def __init__(self, d, component):
157        self.d = d
158        self.iname = '%s.%s' % (self.d.currentIName, component)
159        self.name = None
160
161
162class DumperBase():
163    @staticmethod
164    def warn(message):
165        print('bridgemessage={msg="%s"},' % message.replace('"', '$').encode('latin1'))
166
167    @staticmethod
168    def showException(msg, exType, exValue, exTraceback):
169        DumperBase.warn('**** CAUGHT EXCEPTION: %s ****' % msg)
170        try:
171            import traceback
172            for line in traceback.format_exception(exType, exValue, exTraceback):
173                DumperBase.warn('%s' % line)
174        except:
175            pass
176
177    def timer(self, desc):
178        return Timer(self, desc)
179
180    def __init__(self):
181        self.isCdb = False
182        self.isGdb = False
183        self.isLldb = False
184        self.isCli = False
185
186        # Later set, or not set:
187        self.stringCutOff = 10000
188        self.displayStringLimit = 100
189        self.useTimeStamps = False
190
191        self.output = ''
192        self.typesReported = {}
193        self.typesToReport = {}
194        self.qtNamespaceToReport = None
195        self.qtCustomEventFunc = 0
196        self.qtCustomEventPltFunc = 0
197        self.qtPropertyFunc = 0
198        self.passExceptions = False
199        self.isTesting = False
200
201        self.typeData = {}
202        self.isBigEndian = False
203        self.packCode = '<'
204
205        self.resetCaches()
206        self.resetStats()
207
208        self.childrenPrefix = 'children=['
209        self.childrenSuffix = '],'
210
211        self.dumpermodules = [
212            os.path.splitext(os.path.basename(p))[0] for p in
213            glob.glob(os.path.join(os.path.dirname(__file__), '*types.py'))
214        ]
215
216        # These values are never used, but the variables need to have
217        # some value base for the swapping logic in Children.__enter__()
218        # and Children.__exit__().
219        self.currentIName = None
220        self.currentValue = None
221        self.currentType = None
222        self.currentNumChild = None
223        self.currentMaxNumChild = None
224        self.currentPrintsAddress = True
225        self.currentChildType = None
226        self.currentChildNumChild = None
227        self.registerKnownTypes()
228
229    def setVariableFetchingOptions(self, args):
230        self.resultVarName = args.get('resultvarname', '')
231        self.expandedINames = set(args.get('expanded', []))
232        self.stringCutOff = int(args.get('stringcutoff', 10000))
233        self.displayStringLimit = int(args.get('displaystringlimit', 100))
234        self.typeformats = args.get('typeformats', {})
235        self.formats = args.get('formats', {})
236        self.watchers = args.get('watchers', {})
237        self.useDynamicType = int(args.get('dyntype', '0'))
238        self.useFancy = int(args.get('fancy', '0'))
239        self.forceQtNamespace = int(args.get('forcens', '0'))
240        self.passExceptions = int(args.get('passexceptions', '0'))
241        self.isTesting = int(args.get('testing', '0'))
242        self.showQObjectNames = int(args.get('qobjectnames', '1'))
243        self.nativeMixed = int(args.get('nativemixed', '0'))
244        self.autoDerefPointers = int(args.get('autoderef', '0'))
245        self.useTimeStamps = int(args.get('timestamps', '0'))
246        self.partialVariable = args.get('partialvar', '')
247        self.uninitialized = args.get('uninitialized', [])
248        self.uninitialized = list(map(lambda x: self.hexdecode(x), self.uninitialized))
249        self.partialUpdate = int(args.get('partial', '0'))
250        self.fallbackQtVersion = 0x50200
251        #DumperBase.warn('NAMESPACE: "%s"' % self.qtNamespace())
252        #DumperBase.warn('EXPANDED INAMES: %s' % self.expandedINames)
253        #DumperBase.warn('WATCHERS: %s' % self.watchers)
254
255    def resetPerStepCaches(self):
256        self.perStepCache = {}
257        pass
258
259    def resetCaches(self):
260        # This is a cache mapping from 'type name' to 'display alternatives'.
261        self.qqFormats = {'QVariant (QVariantMap)': [DisplayFormat.CompactMap]}
262
263        # This is a cache of all known dumpers.
264        self.qqDumpers = {}    # Direct type match
265        self.qqDumpersEx = {}  # Using regexp
266
267        # This is a cache of all dumpers that support writing.
268        self.qqEditable = {}
269
270        # This keeps canonical forms of the typenames, without array indices etc.
271        self.cachedFormats = {}
272
273        # Maps type names to static metaobjects. If a type is known
274        # to not be QObject derived, it contains a 0 value.
275        self.knownStaticMetaObjects = {}
276
277        # A dictionary to serve as a per debugging step cache.
278        # Cleared on each step over / into / continue.
279        self.perStepCache = {}
280
281        # A dictionary to serve as a general cache throughout the whole
282        # debug session.
283        self.generalCache = {}
284
285        self.counts = {}
286        self.structPatternCache = {}
287        self.timings = []
288        self.expandableINames = set({})
289
290    def resetStats(self):
291        # Timing collection
292        self.timings = []
293        pass
294
295    def dumpStats(self):
296        msg = [self.counts, self.timings]
297        self.resetStats()
298        return msg
299
300    def bump(self, key):
301        if key in self.counts:
302            self.counts[key] += 1
303        else:
304            self.counts[key] = 1
305
306    def childRange(self):
307        if self.currentMaxNumChild is None:
308            return range(0, self.currentNumChild)
309        return range(min(self.currentMaxNumChild, self.currentNumChild))
310
311    def enterSubItem(self, item):
312        if self.useTimeStamps:
313            item.startTime = time.time()
314        if not item.iname:
315            item.iname = '%s.%s' % (self.currentIName, item.name)
316        if not self.isCli:
317            self.put('{')
318            if isinstance(item.name, str):
319                self.putField('name', item.name)
320        else:
321            self.indent += 1
322            self.output += '\n' + '   ' * self.indent
323            if isinstance(item.name, str):
324                self.output += item.name + ' = '
325        item.savedIName = self.currentIName
326        item.savedValue = self.currentValue
327        item.savedType = self.currentType
328        self.currentIName = item.iname
329        self.currentValue = ReportItem()
330        self.currentType = ReportItem()
331
332    def exitSubItem(self, item, exType, exValue, exTraceBack):
333        #DumperBase.warn('CURRENT VALUE: %s: %s %s' %
334        # (self.currentIName, self.currentValue, self.currentType))
335        if exType is not None:
336            if self.passExceptions:
337                self.showException('SUBITEM', exType, exValue, exTraceBack)
338            self.putSpecialValue('notaccessible')
339            self.putNumChild(0)
340        if not self.isCli:
341            try:
342                if self.currentType.value:
343                    typeName = self.currentType.value
344                    if len(typeName) > 0 and typeName != self.currentChildType:
345                        self.putField('type', typeName)
346                if self.currentValue.value is None:
347                    self.put('value="",encoding="notaccessible",numchild="0",')
348                else:
349                    if self.currentValue.encoding is not None:
350                        self.put('valueencoded="%s",' % self.currentValue.encoding)
351                    if self.currentValue.elided:
352                        self.put('valueelided="%s",' % self.currentValue.elided)
353                    self.put('value="%s",' % self.currentValue.value)
354            except:
355                pass
356            if self.useTimeStamps:
357                self.put('time="%s",' % (time.time() - item.startTime))
358            self.put('},')
359        else:
360            self.indent -= 1
361            try:
362                if self.currentType.value:
363                    typeName = self.currentType.value
364                    self.put('<%s> = {' % typeName)
365
366                if self.currentValue.value is None:
367                    self.put('<not accessible>')
368                else:
369                    value = self.currentValue.value
370                    if self.currentValue.encoding == 'latin1':
371                        value = self.hexdecode(value)
372                    elif self.currentValue.encoding == 'utf8':
373                        value = self.hexdecode(value)
374                    elif self.currentValue.encoding == 'utf16':
375                        b = bytes(bytearray.fromhex(value))
376                        value = codecs.decode(b, 'utf-16')
377                    self.put('"%s"' % value)
378                    if self.currentValue.elided:
379                        self.put('...')
380
381                if self.currentType.value:
382                    self.put('}')
383            except:
384                pass
385        self.currentIName = item.savedIName
386        self.currentValue = item.savedValue
387        self.currentType = item.savedType
388        return True
389
390    def stripForFormat(self, typeName):
391        if not isinstance(typeName, str):
392            raise RuntimeError('Expected string in stripForFormat(), got %s' % type(typeName))
393        if typeName in self.cachedFormats:
394            return self.cachedFormats[typeName]
395        stripped = ''
396        inArray = 0
397        for c in typeName:
398            if c == '<':
399                break
400            if c == ' ':
401                continue
402            if c == '[':
403                inArray += 1
404            elif c == ']':
405                inArray -= 1
406            if inArray and ord(c) >= 48 and ord(c) <= 57:
407                continue
408            stripped += c
409        self.cachedFormats[typeName] = stripped
410        return stripped
411
412    def templateArgument(self, typeobj, position):
413        return typeobj.templateArgument(position)
414
415    def intType(self):
416        result = self.lookupType('int')
417        self.intType = lambda: result
418        return result
419
420    def charType(self):
421        result = self.lookupType('char')
422        self.intType = lambda: result
423        return result
424
425    def ptrSize(self):
426        result = self.lookupType('void*').size()
427        self.ptrSize = lambda: result
428        return result
429
430    def lookupType(self, typeName):
431        nativeType = self.lookupNativeType(typeName)
432        return None if nativeType is None else self.fromNativeType(nativeType)
433
434    def registerKnownTypes(self):
435        typeId = 'unsigned short'
436        tdata = self.TypeData(self)
437        tdata.name = typeId
438        tdata.typeId = typeId
439        tdata.lbitsize = 16
440        tdata.code = TypeCode.Integral
441        self.registerType(typeId, tdata)
442
443        typeId = 'QChar'
444        tdata = self.TypeData(self)
445        tdata.name = typeId
446        tdata.typeId = typeId
447        tdata.lbitsize = 16
448        tdata.code = TypeCode.Struct
449        tdata.lfields = [self.Field(dumper=self, name='ucs',
450                                    type='unsigned short', bitsize=16, bitpos=0)]
451        tdata.lalignment = 2
452        tdata.templateArguments = []
453        self.registerType(typeId, tdata)
454
455    def nativeDynamicType(self, address, baseType):
456        return baseType  # Override in backends.
457
458    def listTemplateParameters(self, typename):
459        return self.listTemplateParametersManually(typename)
460
461    def listTemplateParametersManually(self, typename):
462        targs = []
463        if not typename.endswith('>'):
464            return targs
465
466        def push(inner):
467            # Handle local struct definitions like QList<main(int, char**)::SomeStruct>
468            inner = inner.strip()[::-1]
469            p = inner.find(')::')
470            if p > -1:
471                inner = inner[p + 3:].strip()
472            if inner.startswith('const '):
473                inner = inner[6:].strip()
474            if inner.endswith(' const'):
475                inner = inner[:-6].strip()
476            #DumperBase.warn("FOUND: %s" % inner)
477            targs.append(inner)
478
479        #DumperBase.warn("SPLITTING %s" % typename)
480        level = 0
481        inner = ''
482        for c in typename[::-1]:  # Reversed...
483            #DumperBase.warn("C: %s" % c)
484            if c == '>':
485                if level > 0:
486                    inner += c
487                level += 1
488            elif c == '<':
489                level -= 1
490                if level > 0:
491                    inner += c
492                else:
493                    push(inner)
494                    inner = ''
495                    break
496            elif c == ',':
497                #DumperBase.warn('c: %s level: %s' % (c, level))
498                if level == 1:
499                    push(inner)
500                    inner = ''
501                else:
502                    inner += c
503            else:
504                inner += c
505
506        #DumperBase.warn("TARGS: %s %s" % (typename, targs))
507        res = []
508        for item in targs[::-1]:
509            if len(item) == 0:
510                continue
511            c = ord(item[0])
512            if c in (45, 46) or (c >= 48 and c < 58):  # '-', '.' or digit.
513                if item.find('.') > -1:
514                    res.append(float(item))
515                else:
516                    if item.endswith('l'):
517                        item = item[:-1]
518                    if item.endswith('u'):
519                        item = item[:-1]
520                    val = toInteger(item)
521                    if val > 0x80000000:
522                        val -= 0x100000000
523                    res.append(val)
524            else:
525                res.append(self.Type(self, item))
526        #DumperBase.warn("RES: %s %s" % (typename, [(None if t is None else t.name) for t in res]))
527        return res
528
529    # Hex decoding operating on str, return str.
530    def hexdecode(self, s):
531        if sys.version_info[0] == 2:
532            return s.decode('hex')
533        return bytes.fromhex(s).decode('utf8')
534
535    # Hex encoding operating on str or bytes, return str.
536    def hexencode(self, s):
537        if s is None:
538            s = ''
539        if sys.version_info[0] == 2:
540            if isinstance(s, buffer):
541                return bytes(s).encode('hex')
542            return s.encode('hex')
543        if isinstance(s, str):
544            s = s.encode('utf8')
545        return base64.b16encode(s).decode('utf8')
546
547    def isQt3Support(self):
548        # assume no Qt 3 support by default
549        return False
550
551    # Clamps size to limit.
552    def computeLimit(self, size, limit):
553        if limit == 0:
554            limit = self.displayStringLimit
555        if limit is None or size <= limit:
556            return 0, size
557        return size, limit
558
559    def vectorData(self, value):
560        if self.qtVersion() >= 0x060000:
561            data, size, alloc = self.qArrayData(value)
562        elif self.qtVersion() >= 0x050000:
563            vector_data_ptr = self.extractPointer(value)
564            if self.ptrSize() == 4:
565                (ref, size, alloc, offset) = self.split('IIIp', vector_data_ptr)
566            else:
567                (ref, size, alloc, pad, offset) = self.split('IIIIp', vector_data_ptr)
568            alloc = alloc & 0x7ffffff
569            data = vector_data_ptr + offset
570        else:
571            vector_data_ptr = self.extractPointer(value)
572            (ref, alloc, size) = self.split('III', vector_data_ptr)
573            data = vector_data_ptr + 16
574        self.check(0 <= size and size <= alloc and alloc <= 1000 * 1000 * 1000)
575        return data, size
576
577    def qArrayData(self, value):
578        if self.qtVersion() >= 0x60000:
579            dd, data, size = self.split('ppp', value)
580            if dd:
581                _, _, alloc = self.split('iip', dd)
582            else: # fromRawData
583                alloc = size
584            return data, size, alloc
585        return self.qArrayDataHelper(self.extractPointer(value))
586
587    def qArrayDataHelper(self, array_data_ptr):
588        # array_data_ptr is what is e.g. stored in a QByteArray's d_ptr.
589        if self.qtVersion() >= 0x050000:
590            # QTypedArray:
591            # - QtPrivate::RefCount ref
592            # - int size
593            # - uint alloc : 31, capacityReserved : 1
594            # - qptrdiff offset
595            (ref, size, alloc, offset) = self.split('IIpp', array_data_ptr)
596            alloc = alloc & 0x7ffffff
597            data = array_data_ptr + offset
598            if self.ptrSize() == 4:
599                data = data & 0xffffffff
600            else:
601                data = data & 0xffffffffffffffff
602        elif self.qtVersion() >= 0x040000:
603            # Data:
604            # - QBasicAtomicInt ref;
605            # - int alloc, size;
606            # - [padding]
607            # - char *data;
608            if self.ptrSize() == 4:
609                (ref, alloc, size, data) = self.split('IIIp', array_data_ptr)
610            else:
611                (ref, alloc, size, pad, data) = self.split('IIIIp', array_data_ptr)
612        else:
613            # Data:
614            # - QShared count;
615            # - QChar *unicode
616            # - char *ascii
617            # - uint len: 30
618            (dummy, dummy, dummy, size) = self.split('IIIp', array_data_ptr)
619            size = self.extractInt(array_data_ptr + 3 * self.ptrSize()) & 0x3ffffff
620            alloc = size  # pretend.
621            data = self.extractPointer(array_data_ptr + self.ptrSize())
622        return data, size, alloc
623
624    def encodeStringHelper(self, value, limit):
625        data, size, alloc = self.qArrayData(value)
626        if alloc != 0:
627            self.check(0 <= size and size <= alloc and alloc <= 100 * 1000 * 1000)
628        elided, shown = self.computeLimit(2 * size, 2 * limit)
629        return elided, self.readMemory(data, shown)
630
631    def encodeByteArrayHelper(self, value, limit):
632        data, size, alloc = self.qArrayData(value)
633        if alloc != 0:
634            self.check(0 <= size and size <= alloc and alloc <= 100 * 1000 * 1000)
635        elided, shown = self.computeLimit(size, limit)
636        return elided, self.readMemory(data, shown)
637
638    def putCharArrayValue(self, data, size, charSize,
639                          displayFormat=DisplayFormat.Automatic):
640        bytelen = size * charSize
641        elided, shown = self.computeLimit(bytelen, self.displayStringLimit)
642        mem = self.readMemory(data, shown)
643        if charSize == 1:
644            if displayFormat in (DisplayFormat.Latin1String, DisplayFormat.SeparateLatin1String):
645                encodingType = 'latin1'
646            else:
647                encodingType = 'utf8'
648            #childType = 'char'
649        elif charSize == 2:
650            encodingType = 'utf16'
651            #childType = 'short'
652        else:
653            encodingType = 'ucs4'
654            #childType = 'int'
655
656        self.putValue(mem, encodingType, elided=elided)
657
658        if displayFormat in (
659                DisplayFormat.SeparateLatin1String,
660                DisplayFormat.SeparateUtf8String,
661                DisplayFormat.Separate):
662            elided, shown = self.computeLimit(bytelen, 100000)
663            self.putDisplay(encodingType + ':separate', self.readMemory(data, shown))
664
665    def putCharArrayHelper(self, data, size, charType,
666                           displayFormat=DisplayFormat.Automatic,
667                           makeExpandable=True):
668        charSize = charType.size()
669        self.putCharArrayValue(data, size, charSize, displayFormat=displayFormat)
670
671        if makeExpandable:
672            self.putNumChild(size)
673            if self.isExpanded():
674                with Children(self):
675                    for i in range(size):
676                        self.putSubItem(size, self.createValue(data + i * charSize, charType))
677
678    def readMemory(self, addr, size):
679        return self.hexencode(bytes(self.readRawMemory(addr, size)))
680
681    def encodeByteArray(self, value, limit=0):
682        elided, data = self.encodeByteArrayHelper(value, limit)
683        return data
684
685    def putByteArrayValue(self, value):
686        elided, data = self.encodeByteArrayHelper(value, self.displayStringLimit)
687        self.putValue(data, 'latin1', elided=elided)
688
689    def encodeString(self, value, limit=0):
690        elided, data = self.encodeStringHelper(value, limit)
691        return data
692
693    def encodedUtf16ToUtf8(self, s):
694        return ''.join([chr(int(s[i:i + 2], 16)) for i in range(0, len(s), 4)])
695
696    def encodeStringUtf8(self, value, limit=0):
697        return self.encodedUtf16ToUtf8(self.encodeString(value, limit))
698
699    def stringData(self, value): # -> (data, size, alloc)
700        return self.qArrayData(value)
701
702    def extractTemplateArgument(self, typename, position):
703        level = 0
704        skipSpace = False
705        inner = ''
706        for c in typename[typename.find('<') + 1: -1]:
707            if c == '<':
708                inner += c
709                level += 1
710            elif c == '>':
711                level -= 1
712                inner += c
713            elif c == ',':
714                if level == 0:
715                    if position == 0:
716                        return inner.strip()
717                    position -= 1
718                    inner = ''
719                else:
720                    inner += c
721                    skipSpace = True
722            else:
723                if skipSpace and c == ' ':
724                    pass
725                else:
726                    inner += c
727                    skipSpace = False
728        # Handle local struct definitions like QList<main(int, char**)::SomeStruct>
729        inner = inner.strip()
730        p = inner.find(')::')
731        if p > -1:
732            inner = inner[p + 3:]
733        return inner
734
735    def putStringValue(self, value):
736        elided, data = self.encodeStringHelper(value, self.displayStringLimit)
737        self.putValue(data, 'utf16', elided=elided)
738
739    def putPtrItem(self, name, value):
740        with SubItem(self, name):
741            self.putValue('0x%x' % value)
742            self.putType('void*')
743
744    def putIntItem(self, name, value):
745        with SubItem(self, name):
746            if isinstance(value, self.Value):
747                self.putValue(value.display())
748            else:
749                self.putValue(value)
750            self.putType('int')
751
752    def putEnumItem(self, name, ival, typish):
753        buf = bytearray(struct.pack('i', ival))
754        val = self.Value(self)
755        val.ldata = bytes(buf)
756        val._type = self.createType(typish)
757        with SubItem(self, name):
758            self.putItem(val)
759
760    def putBoolItem(self, name, value):
761        with SubItem(self, name):
762            self.putValue(value)
763            self.putType('bool')
764
765    def putPairItem(self, index, pair, keyName='first', valueName='second'):
766        with SubItem(self, index):
767            self.putPairContents(index, pair, keyName, valueName)
768
769    def putPairContents(self, index, pair, kname, vname):
770        with Children(self):
771            first, second = pair if isinstance(pair, tuple) else pair.members(False)
772            key = self.putSubItem(kname, first)
773            value = self.putSubItem(vname, second)
774        if self.isCli:
775            self.putEmptyValue()
776        else:
777            if index is not None:
778                self.putField('keyprefix', '[%s] ' % index)
779            self.putField('key', key.value)
780            if key.encoding is not None:
781                self.putField('keyencoded', key.encoding)
782            self.putValue(value.value, value.encoding)
783
784    def putEnumValue(self, ival, vals):
785        nice = vals.get(ival, None)
786        display = ('%d' % ival) if nice is None else ('%s (%d)' % (nice, ival))
787        self.putValue(display)
788
789    def putCallItem(self, name, rettype, value, func, *args):
790        with SubItem(self, name):
791            try:
792                result = self.callHelper(rettype, value, func, args)
793            except Exception as error:
794                if self.passExceptions:
795                    raise error
796                children = [('error', error)]
797                self.putSpecialValue("notcallable", children=children)
798            else:
799                self.putItem(result)
800
801    def call(self, rettype, value, func, *args):
802        return self.callHelper(rettype, value, func, args)
803
804    def putAddress(self, address):
805        if address is not None and not self.isCli:
806            self.put('address="0x%x",' % address)
807
808    def putPlainChildren(self, value, dumpBase=True):
809        self.putExpandable()
810        if self.isExpanded():
811            self.putEmptyValue(-99)
812            with Children(self):
813                self.putFields(value, dumpBase)
814
815    def putNamedChildren(self, values, names):
816        self.putEmptyValue(-99)
817        self.putExpandable()
818        if self.isExpanded():
819            with Children(self):
820                for n, v in zip(names, values):
821                    self.putSubItem(n, v)
822
823    def prettySymbolByAddress(self, address):
824        return '0x%x' % address
825
826    def putSymbolValue(self, address):
827        self.putValue(self.prettySymbolByAddress(address))
828
829    def putVTableChildren(self, item, itemCount):
830        p = item.pointer()
831        for i in range(itemCount):
832            deref = self.extractPointer(p)
833            if deref == 0:
834                itemCount = i
835                break
836            with SubItem(self, i):
837                self.putItem(self.createPointerValue(deref, 'void'))
838                p += self.ptrSize()
839        return itemCount
840
841    def putFields(self, value, dumpBase=True):
842        baseIndex = 0
843        for item in value.members(True):
844            if item.name is not None:
845                if item.name.startswith('_vptr.') or item.name.startswith('__vfptr'):
846                    with SubItem(self, '[vptr]'):
847                        # int (**)(void)
848                        self.putType(' ')
849                        self.putSortGroup(20)
850                        self.putValue(item.name)
851                        n = 100
852                        if self.isExpanded():
853                            with Children(self):
854                                n = self.putVTableChildren(item, n)
855                        self.putNumChild(n)
856                    continue
857
858            if item.isBaseClass and dumpBase:
859                baseIndex += 1
860                # We cannot use nativeField.name as part of the iname as
861                # it might contain spaces and other strange characters.
862                with UnnamedSubItem(self, "@%d" % baseIndex):
863                    self.putField('iname', self.currentIName)
864                    self.putField('name', '[%s]' % item.name)
865                    if not self.isCli:
866                        self.putSortGroup(1000 - baseIndex)
867                        self.putAddress(item.address())
868                    self.putItem(item)
869                continue
870
871            with SubItem(self, item.name):
872                self.putItem(item)
873
874    def putExpandable(self):
875        self.putNumChild(1)
876        self.expandableINames.add(self.currentIName)
877        if self.isCli:
878            self.putValue('{...}', -99)
879
880    def putMembersItem(self, value, sortorder=10):
881        with SubItem(self, '[members]'):
882            self.putSortGroup(sortorder)
883            self.putPlainChildren(value)
884
885    def put(self, stuff):
886        self.output += stuff
887
888    def check(self, exp):
889        if not exp:
890            raise RuntimeError('Check failed: %s' % exp)
891
892    def checkRef(self, ref):
893        # Assume there aren't a million references to any object.
894        self.check(ref >= -1)
895        self.check(ref < 1000000)
896
897    def checkIntType(self, thing):
898        if not self.isInt(thing):
899            raise RuntimeError('Expected an integral value, got %s' % type(thing))
900
901    def readToFirstZero(self, base, tsize, maximum):
902        self.checkIntType(base)
903        self.checkIntType(tsize)
904        self.checkIntType(maximum)
905
906        code = self.packCode + (None, 'b', 'H', None, 'I')[tsize]
907        #blob = self.readRawMemory(base, 1)
908        blob = bytes()
909        while maximum > 1:
910            try:
911                blob = self.readRawMemory(base, maximum)
912                break
913            except:
914                maximum = int(maximum / 2)
915                self.warn('REDUCING READING MAXIMUM TO %s' % maximum)
916
917        #DumperBase.warn('BASE: 0x%x TSIZE: %s MAX: %s' % (base, tsize, maximum))
918        for i in range(0, maximum, tsize):
919            t = struct.unpack_from(code, blob, i)[0]
920            if t == 0:
921                return 0, i, self.hexencode(blob[:i])
922
923        # Real end is unknown.
924        return -1, maximum, self.hexencode(blob[:maximum])
925
926    def encodeCArray(self, p, tsize, limit):
927        elided, shown, blob = self.readToFirstZero(p, tsize, limit)
928        return elided, blob
929
930    def putItemCount(self, count, maximum=1000000000):
931        # This needs to override the default value, so don't use 'put' directly.
932        if count > maximum:
933            self.putSpecialValue('minimumitemcount', maximum)
934        else:
935            self.putSpecialValue('itemcount', count)
936        self.putNumChild(count)
937
938    def resultToMi(self, value):
939        if isinstance(value, bool):
940            return '"%d"' % int(value)
941        if isinstance(value, dict):
942            return '{' + ','.join(['%s=%s' % (k, self.resultToMi(v))
943                                   for (k, v) in list(value.items())]) + '}'
944        if isinstance(value, list):
945            return '[' + ','.join([self.resultToMi(k)
946                                   for k in value]) + ']'
947        return '"%s"' % value
948
949    def variablesToMi(self, value, prefix):
950        if isinstance(value, bool):
951            return '"%d"' % int(value)
952        if isinstance(value, dict):
953            pairs = []
954            for (k, v) in list(value.items()):
955                if k == 'iname':
956                    if v.startswith('.'):
957                        v = '"%s%s"' % (prefix, v)
958                    else:
959                        v = '"%s"' % v
960                else:
961                    v = self.variablesToMi(v, prefix)
962                pairs.append('%s=%s' % (k, v))
963            return '{' + ','.join(pairs) + '}'
964        if isinstance(value, list):
965            index = 0
966            pairs = []
967            for item in value:
968                if item.get('type', '') == 'function':
969                    continue
970                name = item.get('name', '')
971                if len(name) == 0:
972                    name = str(index)
973                    index += 1
974                pairs.append((name, self.variablesToMi(item, prefix)))
975            pairs.sort(key=lambda pair: pair[0])
976            return '[' + ','.join([pair[1] for pair in pairs]) + ']'
977        return '"%s"' % value
978
979    def filterPrefix(self, prefix, items):
980        return [i[len(prefix):] for i in items if i.startswith(prefix)]
981
982    def tryFetchInterpreterVariables(self, args):
983        if not int(args.get('nativemixed', 0)):
984            return (False, '')
985        context = args.get('context', '')
986        if not len(context):
987            return (False, '')
988
989        expanded = args.get('expanded')
990        args['expanded'] = self.filterPrefix('local', expanded)
991
992        res = self.sendInterpreterRequest('variables', args)
993        if not res:
994            return (False, '')
995
996        reslist = []
997        for item in res.get('variables', {}):
998            if 'iname' not in item:
999                item['iname'] = '.' + item.get('name')
1000            reslist.append(self.variablesToMi(item, 'local'))
1001
1002        watchers = args.get('watchers', None)
1003        if watchers:
1004            toevaluate = []
1005            name2expr = {}
1006            seq = 0
1007            for watcher in watchers:
1008                expr = self.hexdecode(watcher.get('exp'))
1009                name = str(seq)
1010                toevaluate.append({'name': name, 'expression': expr})
1011                name2expr[name] = expr
1012                seq += 1
1013            args['expressions'] = toevaluate
1014
1015            args['expanded'] = self.filterPrefix('watch', expanded)
1016            del args['watchers']
1017            res = self.sendInterpreterRequest('expressions', args)
1018
1019            if res:
1020                for item in res.get('expressions', {}):
1021                    name = item.get('name')
1022                    iname = 'watch.' + name
1023                    expr = name2expr.get(name)
1024                    item['iname'] = iname
1025                    item['wname'] = self.hexencode(expr)
1026                    item['exp'] = expr
1027                    reslist.append(self.variablesToMi(item, 'watch'))
1028
1029        return (True, 'data=[%s]' % ','.join(reslist))
1030
1031    def putField(self, name, value):
1032        self.put('%s="%s",' % (name, value))
1033
1034    def putType(self, typish, priority=0):
1035        # Higher priority values override lower ones.
1036        if priority >= self.currentType.priority:
1037            types = (str) if sys.version_info[0] >= 3 else (str, unicode)
1038            if isinstance(typish, types):
1039                self.currentType.value = typish
1040            else:
1041                self.currentType.value = typish.name
1042            self.currentType.priority = priority
1043
1044    def putValue(self, value, encoding=None, priority=0, elided=None):
1045        # Higher priority values override lower ones.
1046        # elided = 0 indicates all data is available in value,
1047        # otherwise it's the true length.
1048        if priority >= self.currentValue.priority:
1049            self.currentValue = ReportItem(value, encoding, priority, elided)
1050
1051    def putSpecialValue(self, encoding, value='', children=None):
1052        self.putValue(value, encoding)
1053        if children is not None:
1054            self.putExpandable()
1055            if self.isExpanded():
1056                with Children(self):
1057                    for name, value in children:
1058                        with SubItem(self, name):
1059                            self.putValue(str(value).replace('"', '$'))
1060
1061    def putEmptyValue(self, priority=-10):
1062        if priority >= self.currentValue.priority:
1063            self.currentValue = ReportItem('', None, priority, None)
1064
1065    def putName(self, name):
1066        self.putField('name', name)
1067
1068    def putBetterType(self, typish):
1069        if isinstance(typish, ReportItem):
1070            self.currentType.value = typish.value
1071        elif isinstance(typish, str):
1072            self.currentType.value = typish.replace('@', self.qtNamespace())
1073        else:
1074            self.currentType.value = typish.name
1075        self.currentType.priority += 1
1076
1077    def putNoType(self):
1078        # FIXME: replace with something that does not need special handling
1079        # in SubItem.__exit__().
1080        self.putBetterType(' ')
1081
1082    def putInaccessible(self):
1083        #self.putBetterType(' ')
1084        self.putNumChild(0)
1085        self.currentValue.value = None
1086
1087    def putNamedSubItem(self, component, value, name):
1088        with SubItem(self, component):
1089            self.putName(name)
1090            self.putItem(value)
1091
1092    def isExpanded(self):
1093        #DumperBase.warn('IS EXPANDED: %s in %s: %s' % (self.currentIName,
1094        #    self.expandedINames, self.currentIName in self.expandedINames))
1095        return self.currentIName in self.expandedINames
1096
1097    def mangleName(self, typeName):
1098        return '_ZN%sE' % ''.join(map(lambda x: '%d%s' % (len(x), x),
1099                                      typeName.split('::')))
1100
1101    def arrayItemCountFromTypeName(self, typeName, fallbackMax=1):
1102        itemCount = typeName[typeName.find('[') + 1:typeName.find(']')]
1103        return int(itemCount) if itemCount else fallbackMax
1104
1105    def putCStyleArray(self, value):
1106        arrayType = value.type.unqualified()
1107        innerType = arrayType.ltarget
1108        if innerType is None:
1109            innerType = value.type.target().unqualified()
1110        address = value.address()
1111        if address:
1112            self.putValue('@0x%x' % address, priority=-1)
1113        else:
1114            self.putEmptyValue()
1115        self.putType(arrayType)
1116
1117        displayFormat = self.currentItemFormat()
1118        arrayByteSize = arrayType.size()
1119        if arrayByteSize == 0:
1120            # This should not happen. But it does, see QTCREATORBUG-14755.
1121            # GDB/GCC produce sizeof == 0 for QProcess arr[3]
1122            # And in the Nim string dumper.
1123            itemCount = self.arrayItemCountFromTypeName(value.type.name, 100)
1124            arrayByteSize = int(itemCount) * innerType.size()
1125
1126        n = arrayByteSize // innerType.size()
1127        p = value.address()
1128        if displayFormat != DisplayFormat.Raw and p:
1129            if innerType.name in (
1130                'char',
1131                'wchar_t',
1132                'unsigned char',
1133                'signed char',
1134                'CHAR',
1135                'WCHAR'
1136            ):
1137                self.putCharArrayHelper(p, n, innerType, self.currentItemFormat(),
1138                                        makeExpandable=False)
1139            else:
1140                self.tryPutSimpleFormattedPointer(p, arrayType, innerType,
1141                                                  displayFormat, arrayByteSize)
1142        self.putNumChild(n)
1143
1144        if self.isExpanded():
1145            self.putArrayData(p, n, innerType)
1146
1147        self.putPlotDataHelper(p, n, innerType)
1148
1149    def cleanAddress(self, addr):
1150        if addr is None:
1151            return '<no address>'
1152        return '0x%x' % toInteger(hex(addr), 16)
1153
1154    def stripNamespaceFromType(self, typeName):
1155        ns = self.qtNamespace()
1156        if len(ns) > 0 and typeName.startswith(ns):
1157            typeName = typeName[len(ns):]
1158        # DumperBase.warn( 'stripping %s' % typeName )
1159        lvl = 0
1160        pos = None
1161        stripChunks = []
1162        sz = len(typeName)
1163        for index in range(0, sz):
1164            s = typeName[index]
1165            if s == '<':
1166                lvl += 1
1167                if lvl == 1:
1168                    pos = index
1169                continue
1170            elif s == '>':
1171                lvl -= 1
1172                if lvl < 0:
1173                    raise RuntimeError("Unbalanced '<' in type, @index %d" % index)
1174                if lvl == 0:
1175                    stripChunks.append((pos, index + 1))
1176        if lvl != 0:
1177            raise RuntimeError("unbalanced at end of type name")
1178        for (f, l) in reversed(stripChunks):
1179            typeName = typeName[:f] + typeName[l:]
1180        return typeName
1181
1182    def tryPutPrettyItem(self, typeName, value):
1183        value.check()
1184        if self.useFancy and self.currentItemFormat() != DisplayFormat.Raw:
1185            self.putType(typeName)
1186
1187            nsStrippedType = self.stripNamespaceFromType(typeName)\
1188                .replace('::', '__')
1189
1190            # Strip leading 'struct' for C structs
1191            if nsStrippedType.startswith('struct '):
1192                nsStrippedType = nsStrippedType[7:]
1193
1194            #DumperBase.warn('STRIPPED: %s' % nsStrippedType)
1195            # The following block is only needed for D.
1196            if nsStrippedType.startswith('_A'):
1197                # DMD v2.058 encodes string[] as _Array_uns long long.
1198                # With spaces.
1199                if nsStrippedType.startswith('_Array_'):
1200                    qdump_Array(self, value)
1201                    return True
1202                if nsStrippedType.startswith('_AArray_'):
1203                    qdump_AArray(self, value)
1204                    return True
1205
1206            dumper = self.qqDumpers.get(nsStrippedType)
1207            #DumperBase.warn('DUMPER: %s' % dumper)
1208            if dumper is not None:
1209                dumper(self, value)
1210                return True
1211
1212            for pattern in self.qqDumpersEx.keys():
1213                dumper = self.qqDumpersEx[pattern]
1214                if re.match(pattern, nsStrippedType):
1215                    dumper(self, value)
1216                    return True
1217
1218        return False
1219
1220    def putSimpleCharArray(self, base, size=None):
1221        if size is None:
1222            elided, shown, data = self.readToFirstZero(base, 1, self.displayStringLimit)
1223        else:
1224            elided, shown = self.computeLimit(int(size), self.displayStringLimit)
1225            data = self.readMemory(base, shown)
1226        self.putValue(data, 'latin1', elided=elided)
1227
1228    def putDisplay(self, editFormat, value):
1229        self.putField('editformat', editFormat)
1230        self.putField('editvalue', value)
1231
1232    # This is shared by pointer and array formatting.
1233    def tryPutSimpleFormattedPointer(self, ptr, typeName, innerType, displayFormat, limit):
1234        if displayFormat == DisplayFormat.Automatic:
1235            targetType = innerType
1236            if innerType.code == TypeCode.Typedef:
1237                targetType = innerType.ltarget
1238
1239            if targetType.name in ('char', 'signed char', 'unsigned char', 'CHAR'):
1240                # Use UTF-8 as default for char *.
1241                self.putType(typeName)
1242                (elided, shown, data) = self.readToFirstZero(ptr, 1, limit)
1243                self.putValue(data, 'utf8', elided=elided)
1244                if self.isExpanded():
1245                    self.putArrayData(ptr, shown, innerType)
1246                return True
1247
1248            if targetType.name in ('wchar_t', 'WCHAR'):
1249                self.putType(typeName)
1250                charSize = self.lookupType('wchar_t').size()
1251                (elided, data) = self.encodeCArray(ptr, charSize, limit)
1252                if charSize == 2:
1253                    self.putValue(data, 'utf16', elided=elided)
1254                else:
1255                    self.putValue(data, 'ucs4', elided=elided)
1256                return True
1257
1258        if displayFormat == DisplayFormat.Latin1String:
1259            self.putType(typeName)
1260            (elided, data) = self.encodeCArray(ptr, 1, limit)
1261            self.putValue(data, 'latin1', elided=elided)
1262            return True
1263
1264        if displayFormat == DisplayFormat.SeparateLatin1String:
1265            self.putType(typeName)
1266            (elided, data) = self.encodeCArray(ptr, 1, limit)
1267            self.putValue(data, 'latin1', elided=elided)
1268            self.putDisplay('latin1:separate', data)
1269            return True
1270
1271        if displayFormat == DisplayFormat.Utf8String:
1272            self.putType(typeName)
1273            (elided, data) = self.encodeCArray(ptr, 1, limit)
1274            self.putValue(data, 'utf8', elided=elided)
1275            return True
1276
1277        if displayFormat == DisplayFormat.SeparateUtf8String:
1278            self.putType(typeName)
1279            (elided, data) = self.encodeCArray(ptr, 1, limit)
1280            self.putValue(data, 'utf8', elided=elided)
1281            self.putDisplay('utf8:separate', data)
1282            return True
1283
1284        if displayFormat == DisplayFormat.Local8BitString:
1285            self.putType(typeName)
1286            (elided, data) = self.encodeCArray(ptr, 1, limit)
1287            self.putValue(data, 'local8bit', elided=elided)
1288            return True
1289
1290        if displayFormat == DisplayFormat.Utf16String:
1291            self.putType(typeName)
1292            (elided, data) = self.encodeCArray(ptr, 2, limit)
1293            self.putValue(data, 'utf16', elided=elided)
1294            return True
1295
1296        if displayFormat == DisplayFormat.Ucs4String:
1297            self.putType(typeName)
1298            (elided, data) = self.encodeCArray(ptr, 4, limit)
1299            self.putValue(data, 'ucs4', elided=elided)
1300            return True
1301
1302        return False
1303
1304    def putFormattedPointer(self, value):
1305        #with self.timer('formattedPointer'):
1306        self.putFormattedPointerX(value)
1307
1308    def putDerefedPointer(self, value):
1309        derefValue = value.dereference()
1310        innerType = value.type.target()  # .unqualified()
1311        self.putType(innerType)
1312        savedCurrentChildType = self.currentChildType
1313        self.currentChildType = innerType.name
1314        derefValue.name = '*'
1315        derefValue.autoDerefCount = value.autoDerefCount + 1
1316
1317        if derefValue.type.code != TypeCode.Pointer:
1318            self.putField('autoderefcount', '{}'.format(derefValue.autoDerefCount))
1319
1320        self.putItem(derefValue)
1321        self.currentChildType = savedCurrentChildType
1322
1323    def putFormattedPointerX(self, value):
1324        self.putOriginalAddress(value.address())
1325        #DumperBase.warn("PUT FORMATTED: %s" % value)
1326        pointer = value.pointer()
1327        self.putAddress(pointer)
1328        #DumperBase.warn('POINTER: 0x%x' % pointer)
1329        if pointer == 0:
1330            #DumperBase.warn('NULL POINTER')
1331            self.putType(value.type)
1332            self.putValue('0x0')
1333            return
1334
1335        typeName = value.type.name
1336
1337        try:
1338            self.readRawMemory(pointer, 1)
1339        except:
1340            # Failure to dereference a pointer should at least
1341            # show the value of a pointer.
1342            #DumperBase.warn('BAD POINTER: %s' % value)
1343            self.putValue('0x%x' % pointer)
1344            self.putType(typeName)
1345            return
1346
1347        if self.currentIName.endswith('.this'):
1348            self.putDerefedPointer(value)
1349            return
1350
1351        displayFormat = self.currentItemFormat(value.type.name)
1352        innerType = value.type.target()  # .unqualified()
1353
1354        if innerType.name == 'void':
1355            #DumperBase.warn('VOID POINTER: %s' % displayFormat)
1356            self.putType(typeName)
1357            self.putSymbolValue(pointer)
1358            return
1359
1360        if displayFormat == DisplayFormat.Raw:
1361            # Explicitly requested bald pointer.
1362            #DumperBase.warn('RAW')
1363            self.putType(typeName)
1364            self.putValue('0x%x' % pointer)
1365            self.putExpandable()
1366            if self.currentIName in self.expandedINames:
1367                with Children(self):
1368                    with SubItem(self, '*'):
1369                        self.putItem(value.dereference())
1370            return
1371
1372        limit = self.displayStringLimit
1373        if displayFormat in (DisplayFormat.SeparateLatin1String, DisplayFormat.SeparateUtf8String):
1374            limit = 1000000
1375        if self.tryPutSimpleFormattedPointer(pointer, typeName,
1376                                             innerType, displayFormat, limit):
1377            self.putExpandable()
1378            return
1379
1380        if DisplayFormat.Array10 <= displayFormat and displayFormat <= DisplayFormat.Array1000:
1381            n = (10, 100, 1000, 10000)[displayFormat - DisplayFormat.Array10]
1382            self.putType(typeName)
1383            self.putItemCount(n)
1384            self.putArrayData(value.pointer(), n, innerType)
1385            return
1386
1387        if innerType.code == TypeCode.Function:
1388            # A function pointer.
1389            self.putSymbolValue(pointer)
1390            self.putType(typeName)
1391            return
1392
1393        #DumperBase.warn('AUTODEREF: %s' % self.autoDerefPointers)
1394        #DumperBase.warn('INAME: %s' % self.currentIName)
1395        #DumperBase.warn('INNER: %s' % innerType.name)
1396        if self.autoDerefPointers:
1397            # Generic pointer type with AutomaticFormat, but never dereference char types:
1398            if innerType.name not in (
1399                'char',
1400                'signed char',
1401                'unsigned char',
1402                'wchar_t',
1403                'CHAR',
1404                'WCHAR'
1405            ):
1406                self.putDerefedPointer(value)
1407                return
1408
1409        #DumperBase.warn('GENERIC PLAIN POINTER: %s' % value.type)
1410        #DumperBase.warn('ADDR PLAIN POINTER: 0x%x' % value.laddress)
1411        self.putType(typeName)
1412        self.putSymbolValue(pointer)
1413        self.putExpandable()
1414        if self.currentIName in self.expandedINames:
1415            with Children(self):
1416                with SubItem(self, '*'):
1417                    self.putItem(value.dereference())
1418
1419    def putOriginalAddress(self, address):
1420        if address is not None:
1421            self.put('origaddr="0x%x",' % address)
1422
1423    def putQObjectNameValue(self, value):
1424        try:
1425            # dd = value['d_ptr']['d'] is just behind the vtable.
1426            (vtable, dd) = self.split('pp', value)
1427            if not self.couldBeQObjectVTable(vtable):
1428                return False
1429
1430            intSize = 4
1431            ptrSize = self.ptrSize()
1432            if self.qtVersion() >= 0x060000:
1433                # Size of QObjectData: 9 pointer + 2 int
1434                #   - vtable
1435                #   - QObject *q_ptr;
1436                #   - QObject *parent;
1437                #   - QObjectList children;
1438                #   - uint isWidget : 1; etc...
1439                #   - int postedEvents;
1440                #   - QDynamicMetaObjectData *metaObject;
1441                #   - QBindingStorage bindingStorage;
1442                extra = self.extractPointer(dd + 9 * ptrSize + 2 * intSize)
1443                if extra == 0:
1444                    return False
1445
1446                # Offset of objectName in ExtraData: 12 pointer
1447                #   - QList<QByteArray> propertyNames;
1448                #   - QList<QVariant> propertyValues;
1449                #   - QVector<int> runningTimers;
1450                #   - QList<QPointer<QObject> > eventFilters;
1451                #   - QString objectName
1452                objectNameAddress = extra + 12 * ptrSize
1453            elif self.qtVersion() >= 0x050000:
1454                # Size of QObjectData: 5 pointer + 2 int
1455                #   - vtable
1456                #   - QObject *q_ptr;
1457                #   - QObject *parent;
1458                #   - QObjectList children;
1459                #   - uint isWidget : 1; etc...
1460                #   - int postedEvents;
1461                #   - QDynamicMetaObjectData *metaObject;
1462                extra = self.extractPointer(dd + 5 * ptrSize + 2 * intSize)
1463                if extra == 0:
1464                    return False
1465
1466                # Offset of objectName in ExtraData: 6 pointer
1467                #   - QVector<QObjectUserData *> userData; only #ifndef QT_NO_USERDATA
1468                #   - QList<QByteArray> propertyNames;
1469                #   - QList<QVariant> propertyValues;
1470                #   - QVector<int> runningTimers;
1471                #   - QList<QPointer<QObject> > eventFilters;
1472                #   - QString objectName
1473                objectNameAddress = extra + 5 * ptrSize
1474            else:
1475                # Size of QObjectData: 5 pointer + 2 int
1476                #  - vtable
1477                #   - QObject *q_ptr;
1478                #   - QObject *parent;
1479                #   - QObjectList children;
1480                #   - uint isWidget : 1; etc..
1481                #   - int postedEvents;
1482                #   - QMetaObject *metaObject;
1483
1484                # Offset of objectName in QObjectPrivate: 5 pointer + 2 int
1485                #   - [QObjectData base]
1486                #   - QString objectName
1487                objectNameAddress = dd + 5 * ptrSize + 2 * intSize
1488
1489
1490            data, size, alloc = self.qArrayData(objectNameAddress)
1491
1492            # Object names are short, and GDB can crash on to big chunks.
1493            # Since this here is a convenience feature only, limit it.
1494            if size <= 0 or size > 80:
1495                return False
1496
1497            raw = self.readMemory(data, 2 * size)
1498            self.putValue(raw, 'utf16', 1)
1499            return True
1500
1501        except:
1502            #    warn('NO QOBJECT: %s' % value.type)
1503            return False
1504
1505    def couldBePointer(self, p):
1506        if self.ptrSize() == 4:
1507            return p > 100000 and (p & 0x3 == 0)
1508        else:
1509            return p > 100000 and (p & 0x7 == 0) and (p < 0x7fffffffffff)
1510
1511    def couldBeVTableEntry(self, p):
1512        if self.ptrSize() == 4:
1513            return p > 100000 and (p & 0x1 == 0)
1514        else:
1515            return p > 100000 and (p & 0x1 == 0) and (p < 0x7fffffffffff)
1516
1517    def couldBeQObjectPointer(self, objectPtr):
1518        try:
1519            vtablePtr, dd = self.split('pp', objectPtr)
1520        except:
1521            self.bump('nostruct-1')
1522            return False
1523
1524        try:
1525            dvtablePtr, qptr, parentPtr = self.split('ppp', dd)
1526        except:
1527            self.bump('nostruct-2')
1528            return False
1529        # Check d_ptr.d.q_ptr == objectPtr
1530        if qptr != objectPtr:
1531            self.bump('q_ptr')
1532            return False
1533
1534        return self.couldBeQObjectVTable(vtablePtr)
1535
1536    def couldBeQObjectVTable(self, vtablePtr):
1537        def getJumpAddress_x86(dumper, address):
1538            relativeJumpCode = 0xe9
1539            jumpCode = 0xff
1540            try:
1541                data = dumper.readRawMemory(address, 6)
1542            except:
1543                return 0
1544            primaryOpcode = data[0]
1545            if primaryOpcode == relativeJumpCode:
1546                # relative jump on 32 and 64 bit with a 32bit offset
1547                offset = int.from_bytes(data[1:5], byteorder='little')
1548                return address + 5 + offset
1549            if primaryOpcode == jumpCode:
1550                if data[1] != 0x25:  # check for known extended opcode
1551                    return 0
1552                # 0xff25 is a relative jump on 64bit and an absolute jump on 32 bit
1553                if self.ptrSize() == 8:
1554                    offset = int.from_bytes(data[2:6], byteorder='little')
1555                    return address + 6 + offset
1556                else:
1557                    return int.from_bytes(data[2:6], byteorder='little')
1558            return 0
1559
1560        # Do not try to extract a function pointer if there are no values to compare with
1561        if self.qtCustomEventFunc == 0 and self.qtCustomEventPltFunc == 0:
1562            return False
1563
1564        try:
1565            customEventOffset = 8 if self.isMsvcTarget() else 9
1566            customEventFunc = self.extractPointer(vtablePtr + customEventOffset * self.ptrSize())
1567        except:
1568            self.bump('nostruct-3')
1569            return False
1570
1571        if self.isWindowsTarget():
1572            if customEventFunc in (self.qtCustomEventFunc, self.qtCustomEventPltFunc):
1573                return True
1574            # The vtable may point to a function that is just calling the customEvent function
1575            customEventFunc = getJumpAddress_x86(self, customEventFunc)
1576            if customEventFunc in (self.qtCustomEventFunc, self.qtCustomEventPltFunc):
1577                return True
1578            customEventFunc = self.extractPointer(customEventFunc)
1579            if customEventFunc in (self.qtCustomEventFunc, self.qtCustomEventPltFunc):
1580                return True
1581            # If the object is defined in another module there may be another level of indirection
1582            customEventFunc = getJumpAddress_x86(self, customEventFunc)
1583
1584        return customEventFunc in (self.qtCustomEventFunc, self.qtCustomEventPltFunc)
1585
1586#    def extractQObjectProperty(objectPtr):
1587#        vtablePtr = self.extractPointer(objectPtr)
1588#        metaObjectFunc = self.extractPointer(vtablePtr)
1589#        cmd = '((void*(*)(void*))0x%x)((void*)0x%x)' % (metaObjectFunc, objectPtr)
1590#        try:
1591#            #DumperBase.warn('MO CMD: %s' % cmd)
1592#            res = self.parseAndEvaluate(cmd)
1593#            #DumperBase.warn('MO RES: %s' % res)
1594#            self.bump('successfulMetaObjectCall')
1595#            return res.pointer()
1596#        except:
1597#            self.bump('failedMetaObjectCall')
1598#            #DumperBase.warn('COULD NOT EXECUTE: %s' % cmd)
1599#        return 0
1600
1601    def extractMetaObjectPtr(self, objectPtr, typeobj):
1602        """ objectPtr - address of *potential* instance of QObject derived class
1603            typeobj - type of *objectPtr if known, None otherwise. """
1604
1605        if objectPtr is not None:
1606            self.checkIntType(objectPtr)
1607
1608        def extractMetaObjectPtrFromAddress():
1609            #return 0
1610            # FIXME: Calling 'works' but seems to impact memory contents(!)
1611            # in relevant places. One symptom is that object name
1612            # contents 'vanishes' as the reported size of the string
1613            # gets zeroed out(?).
1614            # Try vtable, metaObject() is the first entry.
1615            vtablePtr = self.extractPointer(objectPtr)
1616            metaObjectFunc = self.extractPointer(vtablePtr)
1617            cmd = '((void*(*)(void*))0x%x)((void*)0x%x)' % (metaObjectFunc, objectPtr)
1618            try:
1619                #DumperBase.warn('MO CMD: %s' % cmd)
1620                res = self.parseAndEvaluate(cmd)
1621                #DumperBase.warn('MO RES: %s' % res)
1622                self.bump('successfulMetaObjectCall')
1623                return res.pointer()
1624            except:
1625                self.bump('failedMetaObjectCall')
1626                #DumperBase.warn('COULD NOT EXECUTE: %s' % cmd)
1627            return 0
1628
1629        def extractStaticMetaObjectFromTypeHelper(someTypeObj):
1630            if someTypeObj.isSimpleType():
1631                return 0
1632
1633            typeName = someTypeObj.name
1634            isQObjectProper = typeName == self.qtNamespace() + 'QObject'
1635
1636            # No templates for now.
1637            if typeName.find('<') >= 0:
1638                return 0
1639
1640            result = self.findStaticMetaObject(someTypeObj)
1641
1642            # We need to distinguish Q_OBJECT from Q_GADGET:
1643            # a Q_OBJECT SMO has a non-null superdata (unless it's QObject itself),
1644            # a Q_GADGET SMO has a null superdata (hopefully)
1645            if result and not isQObjectProper:
1646                if self.qtVersion() >= 0x60000 and self.isWindowsTarget():
1647                    (direct, indirect) = self.split('pp', result)
1648                    # since Qt 6 there is an additional indirect super data getter on windows
1649                    if direct == 0 and indirect == 0:
1650                        # This looks like a Q_GADGET
1651                        return 0
1652                else:
1653                    if self.extractPointer(result) == 0:
1654                        # This looks like a Q_GADGET
1655                        return 0
1656
1657            return result
1658
1659        def extractStaticMetaObjectPtrFromType(someTypeObj):
1660            if someTypeObj is None:
1661                return 0
1662            someTypeName = someTypeObj.name
1663            self.bump('metaObjectFromType')
1664            known = self.knownStaticMetaObjects.get(someTypeName, None)
1665            if known is not None:  # Is 0 or the static metaobject.
1666                return known
1667
1668            result = 0
1669            #try:
1670            result = extractStaticMetaObjectFromTypeHelper(someTypeObj)
1671            #except RuntimeError as error:
1672            #    warn('METAOBJECT EXTRACTION FAILED: %s' % error)
1673            #except:
1674            #    warn('METAOBJECT EXTRACTION FAILED FOR UNKNOWN REASON')
1675
1676            #if not result:
1677            #    base = someTypeObj.firstBase()
1678            #    if base is not None and base != someTypeObj: # sanity check
1679            #        result = extractStaticMetaObjectPtrFromType(base)
1680
1681            if result:
1682                self.knownStaticMetaObjects[someTypeName] = result
1683            return result
1684
1685        if not self.useFancy:
1686            return 0
1687
1688        ptrSize = self.ptrSize()
1689
1690        typeName = typeobj.name
1691        result = self.knownStaticMetaObjects.get(typeName, None)
1692        if result is not None:  # Is 0 or the static metaobject.
1693            self.bump('typecached')
1694            #DumperBase.warn('CACHED RESULT: %s %s 0x%x' % (self.currentIName, typeName, result))
1695            return result
1696
1697        if not self.couldBeQObjectPointer(objectPtr):
1698            self.bump('cannotBeQObject')
1699            #DumperBase.warn('DOES NOT LOOK LIKE A QOBJECT: %s' % self.currentIName)
1700            return 0
1701
1702        metaObjectPtr = 0
1703        if not metaObjectPtr:
1704            # measured: 3 ms (example had one level of inheritance)
1705            #with self.timer('metaObjectType-' + self.currentIName):
1706            metaObjectPtr = extractStaticMetaObjectPtrFromType(typeobj)
1707
1708        if not metaObjectPtr and not self.isWindowsTarget():
1709            # measured: 200 ms (example had one level of inheritance)
1710            #with self.timer('metaObjectCall-' + self.currentIName):
1711            metaObjectPtr = extractMetaObjectPtrFromAddress()
1712
1713        #if metaObjectPtr:
1714        #    self.bump('foundMetaObject')
1715        #    self.knownStaticMetaObjects[typeName] = metaObjectPtr
1716
1717        return metaObjectPtr
1718
1719    def split(self, pattern, value):
1720        if isinstance(value, self.Value):
1721            return value.split(pattern)
1722        if self.isInt(value):
1723            val = self.Value(self)
1724            val.laddress = value
1725            return val.split(pattern)
1726        raise RuntimeError('CANNOT EXTRACT STRUCT FROM %s' % type(value))
1727
1728    def extractCString(self, addr):
1729        result = bytearray()
1730        while True:
1731            d = self.extractByte(addr)
1732            if d == 0:
1733                break
1734            result.append(d)
1735            addr += 1
1736        return result
1737
1738    def listData(self, value, check=True):
1739        if self.qtVersion() >= 0x60000:
1740            dd, data, size = self.split('ppi', value)
1741            return data, size
1742
1743        base = self.extractPointer(value)
1744        (ref, alloc, begin, end) = self.split('IIII', base)
1745        array = base + 16
1746        if self.qtVersion() < 0x50000:
1747            array += self.ptrSize()
1748        size = end - begin
1749
1750        if check:
1751            self.check(begin >= 0 and end >= 0 and end <= 1000 * 1000 * 1000)
1752            size = end - begin
1753            self.check(size >= 0)
1754
1755        stepSize = self.ptrSize()
1756        data = array + begin * stepSize
1757        return data, size
1758
1759    def putTypedPointer(self, name, addr, typeName):
1760        """ Prints a typed pointer, expandable if the type can be resolved,
1761            and without children otherwise """
1762        with SubItem(self, name):
1763            self.putAddress(addr)
1764            self.putValue('@0x%x' % addr)
1765            typeObj = self.lookupType(typeName)
1766            if typeObj:
1767                self.putType(typeObj)
1768                self.putExpandable()
1769                if self.isExpanded():
1770                    with Children(self):
1771                        self.putFields(self.createValue(addr, typeObj))
1772            else:
1773                self.putType(typeName)
1774
1775    # This is called is when a QObject derived class is expanded
1776    def tryPutQObjectGuts(self, value):
1777        metaObjectPtr = self.extractMetaObjectPtr(value.address(), value.type)
1778        if metaObjectPtr:
1779            self.putQObjectGutsHelper(value, value.address(),
1780                                      -1, metaObjectPtr, 'QObject')
1781
1782    def metaString(self, metaObjectPtr, index, revision):
1783        ptrSize = self.ptrSize()
1784        stringdataOffset = ptrSize
1785        if self.isWindowsTarget() and self.qtVersion() >= 0x060000:
1786            stringdataOffset += ptrSize # indirect super data member
1787        stringdata = self.extractPointer(toInteger(metaObjectPtr) + stringdataOffset)
1788
1789        def unpackString(base, size):
1790            try:
1791                s = struct.unpack_from('%ds' % size, self.readRawMemory(base, size))[0]
1792                return s if sys.version_info[0] == 2 else s.decode('utf8')
1793            except:
1794                return '<not available>'
1795
1796        if revision >= 9:  # Qt 6.
1797            pos, size = self.split('II', stringdata + 8 * index)
1798            return unpackString(stringdata + pos, size)
1799
1800        if revision >= 7:  # Qt 5.
1801            byteArrayDataSize = 24 if ptrSize == 8 else 16
1802            literal = stringdata + toInteger(index) * byteArrayDataSize
1803            base, size, _ = self.qArrayDataHelper(literal)
1804            return unpackString(base, size)
1805
1806        ldata = stringdata + index
1807        return self.extractCString(ldata).decode('utf8')
1808
1809    def putSortGroup(self, sortorder):
1810        if not self.isCli:
1811            self.putField('sortgroup', sortorder)
1812
1813    def putQMetaStuff(self, value, origType):
1814        if self.qtVersion() >= 0x060000:
1815            metaObjectPtr, handle = value.split('pp')
1816        else:
1817            metaObjectPtr, handle = value.split('pI')
1818        if metaObjectPtr != 0:
1819            if self.qtVersion() >= 0x060000:
1820                if handle == 0:
1821                    self.putEmptyValue()
1822                    return
1823                revision = 9
1824                name, alias, flags, keyCount, data = self.split('IIIII', handle)
1825                index = name
1826            elif self.qtVersion() >= 0x050000:
1827                revision = 7
1828                dataPtr = self.extractPointer(metaObjectPtr + 2 * self.ptrSize())
1829                index = self.extractInt(dataPtr + 4 * handle)
1830            else:
1831                revision = 6
1832                dataPtr = self.extractPointer(metaObjectPtr + 2 * self.ptrSize())
1833                index = self.extractInt(dataPtr + 4 * handle)
1834            #self.putValue("index: %s rev: %s" % (index, revision))
1835            name = self.metaString(metaObjectPtr, index, revision)
1836            self.putValue(name)
1837            self.putExpandable()
1838            if self.isExpanded():
1839                with Children(self):
1840                    self.putFields(value)
1841                    self.putQObjectGutsHelper(0, 0, handle, metaObjectPtr, origType)
1842        else:
1843            self.putEmptyValue()
1844            if self.isExpanded():
1845                with Children(self):
1846                    self.putFields(value)
1847
1848    # basically all meta things go through this here.
1849    # qobject and qobjectPtr are non-null  if coming from a real structure display
1850    # qobject == 0, qobjectPtr != 0 is possible for builds without QObject debug info
1851    #   if qobject == 0, properties and d-ptr cannot be shown.
1852    # handle is what's store in QMetaMethod etc, pass -1 for QObject/QMetaObject
1853    # itself metaObjectPtr needs to point to a valid QMetaObject.
1854    def putQObjectGutsHelper(self, qobject, qobjectPtr, handle, metaObjectPtr, origType):
1855        ptrSize = self.ptrSize()
1856
1857        def putt(name, value, typeName=' '):
1858            with SubItem(self, name):
1859                self.putValue(value)
1860                self.putType(typeName)
1861
1862        def extractSuperDataPtr(someMetaObjectPtr):
1863            #return someMetaObjectPtr['d']['superdata']
1864            return self.extractPointer(someMetaObjectPtr)
1865
1866        def extractDataPtr(someMetaObjectPtr):
1867            # dataPtr = metaObjectPtr['d']['data']
1868            if self.qtVersion() >= 0x60000 and self.isWindowsTarget():
1869                offset = 3
1870            else:
1871                offset = 2
1872            return self.extractPointer(someMetaObjectPtr + offset * ptrSize)
1873
1874        isQMetaObject = origType == 'QMetaObject'
1875        isQObject = origType == 'QObject'
1876
1877        #DumperBase.warn('OBJECT GUTS: %s 0x%x ' % (self.currentIName, metaObjectPtr))
1878        dataPtr = extractDataPtr(metaObjectPtr)
1879        #DumperBase.warn('DATA PTRS: %s 0x%x ' % (self.currentIName, dataPtr))
1880        (revision, classname,
1881            classinfo, classinfo2,
1882            methodCount, methods,
1883            propertyCount, properties,
1884            enumCount, enums,
1885            constructorCount, constructors,
1886            flags, signalCount) = self.split('I' * 14, dataPtr)
1887
1888        largestStringIndex = -1
1889        for i in range(methodCount):
1890            t = self.split('IIIII', dataPtr + 56 + i * 20)
1891            if largestStringIndex < t[0]:
1892                largestStringIndex = t[0]
1893
1894        ns = self.qtNamespace()
1895        extraData = 0
1896        if qobjectPtr:
1897            dd = self.extractPointer(qobjectPtr + ptrSize)
1898            if self.qtVersion() >= 0x60000:
1899                (dvtablePtr, qptr, parent, children, bindingStorageData, bindingStatus,
1900                    flags, postedEvents, dynMetaObjectPtr, # Up to here QObjectData.
1901                    extraData, threadDataPtr, connectionListsPtr,
1902                    sendersPtr, currentSenderPtr) \
1903                    = self.split('pp{@QObject*}{@QList<@QObject *>}ppIIp' + 'ppppp', dd)
1904            elif self.qtVersion() >= 0x50000:
1905                (dvtablePtr, qptr, parent, children, flags, postedEvents,
1906                    dynMetaObjectPtr,  # Up to here QObjectData.
1907                    extraData, threadDataPtr, connectionListsPtr,
1908                    sendersPtr, currentSenderPtr) \
1909                    = self.split('pp{@QObject*}{@QList<@QObject *>}IIp' + 'ppppp', dd)
1910            else:
1911                (dvtablePtr, qptr, parent, children, flags, postedEvents,
1912                    dynMetaObjectPtr,  # Up to here QObjectData
1913                    objectName, extraData, threadDataPtr, connectionListsPtr,
1914                    sendersPtr, currentSenderPtr) \
1915                    = self.split('pp{@QObject*}{@QList<@QObject *>}IIp' + 'pppppp', dd)
1916
1917            with SubItem(self, '[parent]'):
1918                if not self.isCli:
1919                    self.putSortGroup(9)
1920                self.putItem(parent)
1921
1922            with SubItem(self, '[children]'):
1923                if not self.isCli:
1924                    self.putSortGroup(8)
1925
1926                dvtablePtr, qptr, parentPtr, children = self.split('ppp{QList<QObject *>}', dd)
1927                self.putItem(children)
1928
1929        if isQMetaObject:
1930            with SubItem(self, '[strings]'):
1931                if not self.isCli:
1932                    self.putSortGroup(2)
1933                if largestStringIndex > 0:
1934                    self.putSpecialValue('minimumitemcount', largestStringIndex)
1935                    self.putExpandable()
1936                    if self.isExpanded():
1937                        with Children(self, largestStringIndex + 1):
1938                            for i in self.childRange():
1939                                with SubItem(self, i):
1940                                    s = self.metaString(metaObjectPtr, i, revision)
1941                                    self.putValue(self.hexencode(s), 'latin1')
1942                else:
1943                    self.putValue(' ')
1944
1945        if isQMetaObject:
1946            with SubItem(self, '[raw]'):
1947                self.putSortGroup(1)
1948                self.putEmptyValue()
1949                self.putExpandable()
1950                if self.isExpanded():
1951                    with Children(self):
1952                        putt('revision', revision)
1953                        putt('classname', classname)
1954                        putt('classinfo', classinfo)
1955                        putt('methods', '%d %d' % (methodCount, methods))
1956                        putt('properties', '%d %d' % (propertyCount, properties))
1957                        putt('enums/sets', '%d %d' % (enumCount, enums))
1958                        putt('constructors', '%d %d' % (constructorCount, constructors))
1959                        putt('flags', flags)
1960                        putt('signalCount', signalCount)
1961                        for i in range(methodCount):
1962                            t = self.split('IIIII', dataPtr + 56 + i * 20)
1963                            putt('method %d' % i, '%s %s %s %s %s' % t)
1964
1965        if isQObject:
1966            with SubItem(self, '[extra]'):
1967                self.putSortGroup(1)
1968                self.putEmptyValue()
1969                self.putExpandable()
1970                if self.isExpanded():
1971                    with Children(self):
1972                        if extraData:
1973                            self.putTypedPointer('[extraData]', extraData,
1974                                                 ns + 'QObjectPrivate::ExtraData')
1975
1976                        with SubItem(self, '[metaObject]'):
1977                            self.putAddress(metaObjectPtr)
1978                            self.putExpandable()
1979                            if self.isExpanded():
1980                                with Children(self):
1981                                    self.putQObjectGutsHelper(
1982                                        0, 0, -1, metaObjectPtr, 'QMetaObject')
1983
1984                        if False:
1985                            with SubItem(self, '[connections]'):
1986                                if connectionListsPtr:
1987                                    typeName = '@QObjectConnectionListVector'
1988                                    self.putItem(self.createValue(connectionListsPtr, typeName))
1989                                else:
1990                                    self.putItemCount(0)
1991
1992                        if False:
1993                            with SubItem(self, '[signals]'):
1994                                self.putItemCount(signalCount)
1995                                if self.isExpanded():
1996                                    with Children(self):
1997                                        j = -1
1998                                        for i in range(signalCount):
1999                                            t = self.split('IIIII', dataPtr + 56 + 20 * i)
2000                                            flags = t[4]
2001                                            if flags != 0x06:
2002                                                continue
2003                                            j += 1
2004                                            with SubItem(self, j):
2005                                                name = self.metaString(
2006                                                    metaObjectPtr, t[0], revision)
2007                                                self.putType(' ')
2008                                                self.putValue(name)
2009                                                self.putExpandable()
2010                                                with Children(self):
2011                                                    putt('[nameindex]', t[0])
2012                                                    #putt('[type]', 'signal')
2013                                                    putt('[argc]', t[1])
2014                                                    putt('[parameter]', t[2])
2015                                                    putt('[tag]', t[3])
2016                                                    putt('[flags]', t[4])
2017                                                    putt('[localindex]', str(i))
2018                                                    putt('[globalindex]', str(globalOffset + i))
2019                                                    #self.putQObjectConnections(dd)
2020
2021        if isQMetaObject or isQObject:
2022            with SubItem(self, '[properties]'):
2023                self.putSortGroup(5)
2024                if self.isExpanded():
2025                    dynamicPropertyCount = 0
2026                    with Children(self):
2027                        # Static properties.
2028                        for i in range(propertyCount):
2029                            if self.qtVersion() >= 0x60000:
2030                                t = self.split('IIIII', dataPtr + properties * 4 + 20 * i)
2031                            else:
2032                                t = self.split('III', dataPtr + properties * 4 + 12 * i)
2033                            name = self.metaString(metaObjectPtr, t[0], revision)
2034                            if qobject and self.qtPropertyFunc:
2035                                    # LLDB doesn't like calling it on a derived class, possibly
2036                                    # due to type information living in a different shared object.
2037                                    #base = self.createValue(qobjectPtr, '@QObject')
2038                                    #DumperBase.warn("CALL FUNC: 0x%x" % self.qtPropertyFunc)
2039                                cmd = '((QVariant(*)(void*,char*))0x%x)((void*)0x%x,"%s")' \
2040                                    % (self.qtPropertyFunc, qobjectPtr, name)
2041                                try:
2042                                        #DumperBase.warn('PROP CMD: %s' % cmd)
2043                                    res = self.parseAndEvaluate(cmd)
2044                                    #DumperBase.warn('PROP RES: %s' % res)
2045                                except:
2046                                    self.bump('failedMetaObjectCall')
2047                                    putt(name, ' ')
2048                                    continue
2049                                    #DumperBase.warn('COULD NOT EXECUTE: %s' % cmd)
2050                                #self.putCallItem(name, '@QVariant', base, 'property', '"' + name + '"')
2051                                if res is None:
2052                                    self.bump('failedMetaObjectCall2')
2053                                    putt(name, ' ')
2054                                    continue
2055                                self.putSubItem(name, res)
2056                            else:
2057                                putt(name, ' ')
2058
2059                        # Dynamic properties.
2060                        if extraData:
2061                            def list6Generator(addr, innerType):
2062                                data, size = self.listData(addr)
2063                                for i in range(size):
2064                                    yield self.createValue(data + i * innerType.size(), innerType)
2065
2066                            def list5Generator(addr, innerType):
2067                                data, size = self.listData(addr)
2068                                for i in range(size):
2069                                    yield self.createValue(data + i * ptrSize, innerType)
2070
2071                            def vectorGenerator(addr, innerType):
2072                                data, size = self.vectorData(addr)
2073                                for i in range(size):
2074                                    yield self.createValue(data + i * innerType.size(), innerType)
2075
2076                            byteArrayType = self.createType('@QByteArray')
2077                            variantType = self.createType('@QVariant')
2078                            if self.qtVersion() >= 0x60000:
2079                                values = vectorGenerator(extraData + 3 * ptrSize, variantType)
2080                            elif self.qtVersion() >= 0x50600:
2081                                values = vectorGenerator(extraData + 2 * ptrSize, variantType)
2082                            elif self.qtVersion() >= 0x50000:
2083                                values = list5Generator(extraData + 2 * ptrSize, variantType)
2084                            else:
2085                                values = list5Generator(extraData + 2 * ptrSize,
2086                                                        variantType.pointer())
2087
2088                            if self.qtVersion() >= 0x60000:
2089                                names = list6Generator(extraData, byteArrayType)
2090                            else:
2091                                names = list5Generator(extraData + ptrSize, byteArrayType)
2092
2093                            for (k, v) in zip(names, values):
2094                                with SubItem(self, propertyCount + dynamicPropertyCount):
2095                                    if not self.isCli:
2096                                        self.putField('key', self.encodeByteArray(k))
2097                                        self.putField('keyencoded', 'latin1')
2098                                    self.putItem(v)
2099                                    dynamicPropertyCount += 1
2100                    self.putItemCount(propertyCount + dynamicPropertyCount)
2101                else:
2102                    # We need a handle to [x] for the user to expand the item
2103                    # before we know whether there are actual children. Counting
2104                    # them is too expensive.
2105                    self.putSpecialValue('minimumitemcount', propertyCount)
2106                    self.putExpandable()
2107
2108        superDataPtr = extractSuperDataPtr(metaObjectPtr)
2109
2110        globalOffset = 0
2111        superDataIterator = superDataPtr
2112        while superDataIterator:
2113            sdata = extractDataPtr(superDataIterator)
2114            globalOffset += self.extractInt(sdata + 16)  # methodCount member
2115            superDataIterator = extractSuperDataPtr(superDataIterator)
2116
2117        if isQMetaObject or isQObject:
2118            with SubItem(self, '[methods]'):
2119                self.putSortGroup(3)
2120                self.putItemCount(methodCount)
2121                if self.isExpanded():
2122                    with Children(self):
2123                        for i in range(methodCount):
2124                            t = self.split('IIIII', dataPtr + 56 + 20 * i)
2125                            name = self.metaString(metaObjectPtr, t[0], revision)
2126                            with SubItem(self, i):
2127                                self.putValue(name)
2128                                self.putType(' ')
2129                                self.putNumChild(1)
2130                                isSignal = False
2131                                flags = t[4]
2132                                if flags == 0x06:
2133                                    typ = 'signal'
2134                                    isSignal = True
2135                                elif flags == 0x0a:
2136                                    typ = 'slot'
2137                                elif flags == 0x0a:
2138                                    typ = 'invokable'
2139                                else:
2140                                    typ = '<unknown>'
2141                                with Children(self):
2142                                    putt('[nameindex]', t[0])
2143                                    putt('[type]', typ)
2144                                    putt('[argc]', t[1])
2145                                    putt('[parameter]', t[2])
2146                                    putt('[tag]', t[3])
2147                                    putt('[flags]', t[4])
2148                                    putt('[localindex]', str(i))
2149                                    putt('[globalindex]', str(globalOffset + i))
2150
2151        if isQObject:
2152            with SubItem(self, '[d]'):
2153                self.putItem(self.createValue(dd, '@QObjectPrivate'))
2154                self.putSortGroup(15)
2155
2156        if isQMetaObject:
2157            with SubItem(self, '[superdata]'):
2158                self.putSortGroup(12)
2159                if superDataPtr:
2160                    self.putType('@QMetaObject')
2161                    self.putAddress(superDataPtr)
2162                    self.putExpandable()
2163                    if self.isExpanded():
2164                        with Children(self):
2165                            self.putQObjectGutsHelper(0, 0, -1, superDataPtr, 'QMetaObject')
2166                else:
2167                    self.putType('@QMetaObject *')
2168                    self.putValue('0x0')
2169
2170        if handle >= 0:
2171            localIndex = int((handle - methods) / 5)
2172            with SubItem(self, '[localindex]'):
2173                self.putSortGroup(12)
2174                self.putValue(localIndex)
2175            with SubItem(self, '[globalindex]'):
2176                self.putSortGroup(11)
2177                self.putValue(globalOffset + localIndex)
2178
2179    def putQObjectConnections(self, dd):
2180        with SubItem(self, '[connections]'):
2181            ptrSize = self.ptrSize()
2182            self.putNoType()
2183            privateType = self.createType('@QObjectPrivate')
2184            d_ptr = dd.cast(privateType.pointer()).dereference()
2185            connections = d_ptr['connectionLists']
2186            if self.connections.integer() == 0:
2187                self.putItemCount(0)
2188            else:
2189                connections = connections.dereference()
2190                #connections = connections.cast(connections.type.firstBase())
2191                self.putSpecialValue('minimumitemcount', 0)
2192                self.putExpandable()
2193            if self.isExpanded():
2194                pp = 0
2195                with Children(self):
2196                    innerType = connections.type[0]
2197                    # Should check:  innerType == ns::QObjectPrivate::ConnectionList
2198                    data, size = self.vectorData(connections)
2199                    connectionType = self.createType('@QObjectPrivate::Connection')
2200                    for i in range(size):
2201                        first = self.extractPointer(data + i * 2 * ptrSize)
2202                        while first:
2203                            self.putSubItem('%s' % pp,
2204                                            self.createPointerValue(first, connectionType))
2205                            first = self.extractPointer(first + 3 * ptrSize)
2206                            # We need to enforce some upper limit.
2207                            pp += 1
2208                            if pp > 1000:
2209                                break
2210
2211    def currentItemFormat(self, typeName=None):
2212        displayFormat = self.formats.get(self.currentIName, DisplayFormat.Automatic)
2213        if displayFormat == DisplayFormat.Automatic:
2214            if typeName is None:
2215                typeName = self.currentType.value
2216            needle = None if typeName is None else self.stripForFormat(typeName)
2217            displayFormat = self.typeformats.get(needle, DisplayFormat.Automatic)
2218        return displayFormat
2219
2220    def putSubItem(self, component, value):  # -> ReportItem
2221        if not isinstance(value, self.Value):
2222            raise RuntimeError('WRONG VALUE TYPE IN putSubItem: %s' % type(value))
2223        if not isinstance(value.type, self.Type):
2224            raise RuntimeError('WRONG TYPE TYPE IN putSubItem: %s' % type(value.type))
2225        res = None
2226        with SubItem(self, component):
2227            self.putItem(value)
2228            res = self.currentValue
2229        return res  # The 'short' display.
2230
2231    def putArrayData(self, base, n, innerType, childNumChild=None, maxNumChild=10000):
2232        self.checkIntType(base)
2233        self.checkIntType(n)
2234        addrBase = base
2235        innerSize = innerType.size()
2236        self.putNumChild(n)
2237        #DumperBase.warn('ADDRESS: 0x%x INNERSIZE: %s INNERTYPE: %s' % (addrBase, innerSize, innerType))
2238        enc = innerType.simpleEncoding()
2239        if enc:
2240            self.put('childtype="%s",' % innerType.name)
2241            self.put('addrbase="0x%x",' % addrBase)
2242            self.put('addrstep="0x%x",' % innerSize)
2243            self.put('arrayencoding="%s",' % enc)
2244            if n > maxNumChild:
2245                self.put('childrenelided="%s",' % n)  # FIXME: Act on that in frontend
2246                n = maxNumChild
2247            self.put('arraydata="')
2248            self.put(self.readMemory(addrBase, n * innerSize))
2249            self.put('",')
2250        else:
2251            with Children(self, n, innerType, childNumChild, maxNumChild,
2252                          addrBase=addrBase, addrStep=innerSize):
2253                for i in self.childRange():
2254                    self.putSubItem(i, self.createValue(addrBase + i * innerSize, innerType))
2255
2256    def putArrayItem(self, name, addr, n, typeName):
2257        self.checkIntType(addr)
2258        self.checkIntType(n)
2259        with SubItem(self, name):
2260            self.putEmptyValue()
2261            self.putType('%s [%d]' % (typeName, n))
2262            self.putArrayData(addr, n, self.lookupType(typeName))
2263            self.putAddress(addr)
2264
2265    def putPlotDataHelper(self, base, n, innerType, maxNumChild=1000 * 1000):
2266        if n > maxNumChild:
2267            self.putField('plotelided', n)  # FIXME: Act on that in frontend
2268            n = maxNumChild
2269        if self.currentItemFormat() == DisplayFormat.ArrayPlot and innerType.isSimpleType():
2270            enc = innerType.simpleEncoding()
2271            if enc:
2272                self.putField('editencoding', enc)
2273                self.putDisplay('plotdata:separate',
2274                                self.readMemory(base, n * innerType.size()))
2275
2276    def putPlotData(self, base, n, innerType, maxNumChild=1000 * 1000):
2277        self.putPlotDataHelper(base, n, innerType, maxNumChild=maxNumChild)
2278        if self.isExpanded():
2279            self.putArrayData(base, n, innerType, maxNumChild=maxNumChild)
2280
2281    def putSpecialArgv(self, value):
2282        """
2283        Special handling for char** argv.
2284        """
2285        n = 0
2286        p = value
2287        # p is 0 for "optimized out" cases. Or contains rubbish.
2288        try:
2289            if value.integer():
2290                while p.dereference().integer() and n <= 100:
2291                    p += 1
2292                    n += 1
2293        except:
2294            pass
2295
2296        with TopLevelItem(self, 'local.argv'):
2297            self.put('iname="local.argv",name="argv",')
2298            self.putItemCount(n, 100)
2299            self.putType('char **')
2300            if self.currentIName in self.expandedINames:
2301                p = value
2302                with Children(self, n):
2303                    for i in range(n):
2304                        self.putSubItem(i, p.dereference())
2305                        p += 1
2306
2307    def extractPointer(self, value):
2308        try:
2309            if value.type.code == TypeCode.Array:
2310                return value.address()
2311        except:
2312            pass
2313        code = 'I' if self.ptrSize() == 4 else 'Q'
2314        return self.extractSomething(value, code, 8 * self.ptrSize())
2315
2316    def extractInt64(self, value):
2317        return self.extractSomething(value, 'q', 64)
2318
2319    def extractUInt64(self, value):
2320        return self.extractSomething(value, 'Q', 64)
2321
2322    def extractInt(self, value):
2323        return self.extractSomething(value, 'i', 32)
2324
2325    def extractUInt(self, value):
2326        return self.extractSomething(value, 'I', 32)
2327
2328    def extractShort(self, value):
2329        return self.extractSomething(value, 'h', 16)
2330
2331    def extractUShort(self, value):
2332        return self.extractSomething(value, 'H', 16)
2333
2334    def extractByte(self, value):
2335        return self.extractSomething(value, 'b', 8)
2336
2337    def extractSomething(self, value, pattern, bitsize):
2338        if self.isInt(value):
2339            val = self.Value(self)
2340            val.laddress = value
2341            return val.extractSomething(pattern, bitsize)
2342        if isinstance(value, self.Value):
2343            return value.extractSomething(pattern, bitsize)
2344        raise RuntimeError('CANT EXTRACT FROM %s' % type(value))
2345
2346    # Parses a..b and  a.(s).b
2347    def parseRange(self, exp):
2348
2349        # Search for the first unbalanced delimiter in s
2350        def searchUnbalanced(s, upwards):
2351            paran = 0
2352            bracket = 0
2353            if upwards:
2354                open_p, close_p, open_b, close_b = '(', ')', '[', ']'
2355            else:
2356                open_p, close_p, open_b, close_b = ')', '(', ']', '['
2357            for i in range(len(s)):
2358                c = s[i]
2359                if c == open_p:
2360                    paran += 1
2361                elif c == open_b:
2362                    bracket += 1
2363                elif c == close_p:
2364                    paran -= 1
2365                    if paran < 0:
2366                        return i
2367                elif c == close_b:
2368                    bracket -= 1
2369                    if bracket < 0:
2370                        return i
2371            return len(s)
2372
2373        match = re.search(r'(\.)(\(.+?\))?(\.)', exp)
2374        if match:
2375            s = match.group(2)
2376            left_e = match.start(1)
2377            left_s = 1 + left_e - searchUnbalanced(exp[left_e::-1], False)
2378            right_s = match.end(3)
2379            right_e = right_s + searchUnbalanced(exp[right_s:], True)
2380            template = exp[:left_s] + '%s' + exp[right_e:]
2381
2382            a = exp[left_s:left_e]
2383            b = exp[right_s:right_e]
2384
2385            try:
2386                # Allow integral expressions.
2387                ss = self.parseAndEvaluate(s[1:len(s) - 1]).integer() if s else 1
2388                aa = self.parseAndEvaluate(a).integer()
2389                bb = self.parseAndEvaluate(b).integer()
2390                if aa < bb and ss > 0:
2391                    return True, aa, ss, bb + 1, template
2392            except:
2393                pass
2394        return False, 0, 1, 1, exp
2395
2396    def putNumChild(self, numchild):
2397        if numchild != self.currentChildNumChild:
2398            self.putField('numchild', numchild)
2399
2400    def handleLocals(self, variables):
2401        #DumperBase.warn('VARIABLES: %s' % variables)
2402        #with self.timer('locals'):
2403        shadowed = {}
2404        for value in variables:
2405            if value.name == 'argv':
2406                if value.type.code == TypeCode.Pointer:
2407                    if value.type.ltarget.code == TypeCode.Pointer:
2408                        if value.type.ltarget.ltarget.name == 'char':
2409                            self.putSpecialArgv(value)
2410                            continue
2411
2412            name = value.name
2413            if name in shadowed:
2414                level = shadowed[name]
2415                shadowed[name] = level + 1
2416                name += '@%d' % level
2417            else:
2418                shadowed[name] = 1
2419            # A 'normal' local variable or parameter.
2420            iname = value.iname if hasattr(value, 'iname') else 'local.' + name
2421            with TopLevelItem(self, iname):
2422                #with self.timer('all-' + iname):
2423                self.putField('iname', iname)
2424                self.putField('name', name)
2425                self.putItem(value)
2426
2427    def handleWatches(self, args):
2428        #with self.timer('watches'):
2429        for watcher in args.get('watchers', []):
2430            iname = watcher['iname']
2431            exp = self.hexdecode(watcher['exp'])
2432            self.handleWatch(exp, exp, iname)
2433
2434    def handleWatch(self, origexp, exp, iname):
2435        exp = str(exp).strip()
2436        escapedExp = self.hexencode(exp)
2437        #DumperBase.warn('HANDLING WATCH %s -> %s, INAME: "%s"' % (origexp, exp, iname))
2438
2439        # Grouped items separated by semicolon.
2440        if exp.find(';') >= 0:
2441            exps = exp.split(';')
2442            n = len(exps)
2443            with TopLevelItem(self, iname):
2444                self.putField('iname', iname)
2445                #self.putField('wname', escapedExp)
2446                self.putField('name', exp)
2447                self.putField('exp', exp)
2448                self.putItemCount(n)
2449                self.putNoType()
2450            for i in range(n):
2451                self.handleWatch(exps[i], exps[i], '%s.%d' % (iname, i))
2452            return
2453
2454        # Special array index: e.g a[1..199] or a[1.(3).199] for stride 3.
2455        isRange, begin, step, end, template = self.parseRange(exp)
2456        if isRange:
2457            #DumperBase.warn('RANGE: %s %s %s in %s' % (begin, step, end, template))
2458            r = range(begin, end, step)
2459            n = len(r)
2460            with TopLevelItem(self, iname):
2461                self.putField('iname', iname)
2462                #self.putField('wname', escapedExp)
2463                self.putField('name', exp)
2464                self.putField('exp', exp)
2465                self.putItemCount(n)
2466                self.putNoType()
2467                with Children(self, n):
2468                    for i in r:
2469                        e = template % i
2470                        self.handleWatch(e, e, '%s.%s' % (iname, i))
2471            return
2472
2473            # Fall back to less special syntax
2474            #return self.handleWatch(origexp, exp, iname)
2475
2476        with TopLevelItem(self, iname):
2477            self.putField('iname', iname)
2478            self.putField('wname', escapedExp)
2479            try:
2480                value = self.parseAndEvaluate(exp)
2481                self.putItem(value)
2482            except Exception:
2483                self.currentType.value = ' '
2484                self.currentValue.value = '<no such value>'
2485                self.currentChildNumChild = -1
2486                self.currentNumChild = 0
2487                self.putNumChild(0)
2488
2489    def registerDumper(self, funcname, function):
2490        try:
2491            if funcname.startswith('qdump__'):
2492                typename = funcname[7:]
2493                spec = inspect.getargspec(function)
2494                if len(spec.args) == 2:
2495                    self.qqDumpers[typename] = function
2496                elif len(spec.args) == 3 and len(spec.defaults) == 1:
2497                    self.qqDumpersEx[spec.defaults[0]] = function
2498                self.qqFormats[typename] = self.qqFormats.get(typename, [])
2499            elif funcname.startswith('qform__'):
2500                typename = funcname[7:]
2501                try:
2502                    self.qqFormats[typename] = function()
2503                except:
2504                    self.qqFormats[typename] = []
2505            elif funcname.startswith('qedit__'):
2506                typename = funcname[7:]
2507                try:
2508                    self.qqEditable[typename] = function
2509                except:
2510                    pass
2511        except:
2512            pass
2513
2514    def setupDumpers(self, _={}):
2515        self.resetCaches()
2516
2517        for mod in self.dumpermodules:
2518            m = __import__(mod)
2519            dic = m.__dict__
2520            for name in dic.keys():
2521                item = dic[name]
2522                self.registerDumper(name, item)
2523
2524        msg = 'dumpers=['
2525        for key, value in self.qqFormats.items():
2526            editable = ',editable="true"' if key in self.qqEditable else ''
2527            formats = (',formats=\"%s\"' % str(value)[1:-1]) if len(value) else ''
2528            msg += '{type="%s"%s%s},' % (key, editable, formats)
2529        msg += '],'
2530        v = 10000 * sys.version_info[0] + 100 * sys.version_info[1] + sys.version_info[2]
2531        msg += 'python="%d"' % v
2532        return msg
2533
2534    def reloadDumpers(self, args):
2535        for mod in self.dumpermodules:
2536            m = sys.modules[mod]
2537            if sys.version_info[0] >= 3:
2538                import importlib
2539                importlib.reload(m)
2540            else:
2541                reload(m)
2542        self.setupDumpers(args)
2543
2544    def loadDumpers(self, args):
2545        msg = self.setupDumpers()
2546        self.reportResult(msg, args)
2547
2548    def addDumperModule(self, args):
2549        path = args['path']
2550        (head, tail) = os.path.split(path)
2551        sys.path.insert(1, head)
2552        self.dumpermodules.append(os.path.splitext(tail)[0])
2553
2554    def extractQStringFromQDataStream(self, buf, offset):
2555        """ Read a QString from the stream """
2556        size = struct.unpack_from('!I', buf, offset)[0]
2557        offset += 4
2558        string = buf[offset:offset + size].decode('utf-16be')
2559        return (string, offset + size)
2560
2561    def extractQByteArrayFromQDataStream(self, buf, offset):
2562        """ Read a QByteArray from the stream """
2563        size = struct.unpack_from('!I', buf, offset)[0]
2564        offset += 4
2565        string = buf[offset:offset + size].decode('latin1')
2566        return (string, offset + size)
2567
2568    def extractIntFromQDataStream(self, buf, offset):
2569        """ Read an int from the stream """
2570        value = struct.unpack_from('!I', buf, offset)[0]
2571        return (value, offset + 4)
2572
2573    def handleInterpreterMessage(self):
2574        """ Return True if inferior stopped """
2575        resdict = self.fetchInterpreterResult()
2576        return resdict.get('event') == 'break'
2577
2578    def reportInterpreterResult(self, resdict, args):
2579        print('interpreterresult=%s,token="%s"'
2580              % (self.resultToMi(resdict), args.get('token', -1)))
2581
2582    def reportInterpreterAsync(self, resdict, asyncclass):
2583        print('interpreterasync=%s,asyncclass="%s"'
2584              % (self.resultToMi(resdict), asyncclass))
2585
2586    def removeInterpreterBreakpoint(self, args):
2587        res = self.sendInterpreterRequest('removebreakpoint', {'id': args['id']})
2588        return res
2589
2590    def insertInterpreterBreakpoint(self, args):
2591        args['condition'] = self.hexdecode(args.get('condition', ''))
2592        # Will fail if the service is not yet up and running.
2593        response = self.sendInterpreterRequest('setbreakpoint', args)
2594        resdict = args.copy()
2595        bp = None if response is None else response.get('breakpoint', None)
2596        if bp:
2597            resdict['number'] = bp
2598            resdict['pending'] = 0
2599        else:
2600            self.createResolvePendingBreakpointsHookBreakpoint(args)
2601            resdict['number'] = -1
2602            resdict['pending'] = 1
2603            resdict['warning'] = 'Direct interpreter breakpoint insertion failed.'
2604        self.reportInterpreterResult(resdict, args)
2605
2606    def resolvePendingInterpreterBreakpoint(self, args):
2607        self.parseAndEvaluate('qt_qmlDebugEnableService("NativeQmlDebugger")')
2608        response = self.sendInterpreterRequest('setbreakpoint', args)
2609        bp = None if response is None else response.get('breakpoint', None)
2610        resdict = args.copy()
2611        if bp:
2612            resdict['number'] = bp
2613            resdict['pending'] = 0
2614        else:
2615            resdict['number'] = -1
2616            resdict['pending'] = 0
2617            resdict['error'] = 'Pending interpreter breakpoint insertion failed.'
2618        self.reportInterpreterAsync(resdict, 'breakpointmodified')
2619
2620    def fetchInterpreterResult(self):
2621        buf = self.parseAndEvaluate('qt_qmlDebugMessageBuffer')
2622        size = self.parseAndEvaluate('qt_qmlDebugMessageLength')
2623        msg = self.hexdecode(self.readMemory(buf, size))
2624        # msg is a sequence of 'servicename<space>msglen<space>msg' items.
2625        resdict = {}  # Native payload.
2626        while len(msg):
2627            pos0 = msg.index(' ')  # End of service name
2628            pos1 = msg.index(' ', pos0 + 1)  # End of message length
2629            service = msg[0:pos0]
2630            msglen = int(msg[pos0 + 1:pos1])
2631            msgend = pos1 + 1 + msglen
2632            payload = msg[pos1 + 1:msgend]
2633            msg = msg[msgend:]
2634            if service == 'NativeQmlDebugger':
2635                try:
2636                    resdict = json.loads(payload)
2637                    continue
2638                except:
2639                    self.warn('Cannot parse native payload: %s' % payload)
2640            else:
2641                print('interpreteralien=%s'
2642                      % {'service': service, 'payload': self.hexencode(payload)})
2643        try:
2644            expr = 'qt_qmlDebugClearBuffer()'
2645            res = self.parseAndEvaluate(expr)
2646        except RuntimeError as error:
2647            self.warn('Cleaning buffer failed: %s: %s' % (expr, error))
2648
2649        return resdict
2650
2651    def sendInterpreterRequest(self, command, args={}):
2652        encoded = json.dumps({'command': command, 'arguments': args})
2653        hexdata = self.hexencode(encoded)
2654        expr = 'qt_qmlDebugSendDataToService("NativeQmlDebugger","%s")' % hexdata
2655        try:
2656            res = self.parseAndEvaluate(expr)
2657        except RuntimeError as error:
2658            self.warn('Interpreter command failed: %s: %s' % (encoded, error))
2659            return {}
2660        except AttributeError as error:
2661            # Happens with LLDB and 'None' current thread.
2662            self.warn('Interpreter command failed: %s: %s' % (encoded, error))
2663            return {}
2664        if not res:
2665            self.warn('Interpreter command failed: %s ' % encoded)
2666            return {}
2667        return self.fetchInterpreterResult()
2668
2669    def executeStep(self, args):
2670        if self.nativeMixed:
2671            response = self.sendInterpreterRequest('stepin', args)
2672        self.doContinue()
2673
2674    def executeStepOut(self, args):
2675        if self.nativeMixed:
2676            response = self.sendInterpreterRequest('stepout', args)
2677        self.doContinue()
2678
2679    def executeNext(self, args):
2680        if self.nativeMixed:
2681            response = self.sendInterpreterRequest('stepover', args)
2682        self.doContinue()
2683
2684    def executeContinue(self, args):
2685        if self.nativeMixed:
2686            response = self.sendInterpreterRequest('continue', args)
2687        self.doContinue()
2688
2689    def doInsertInterpreterBreakpoint(self, args, wasPending):
2690        #DumperBase.warn('DO INSERT INTERPRETER BREAKPOINT, WAS PENDING: %s' % wasPending)
2691        # Will fail if the service is not yet up and running.
2692        response = self.sendInterpreterRequest('setbreakpoint', args)
2693        bp = None if response is None else response.get('breakpoint', None)
2694        if wasPending:
2695            if not bp:
2696                self.reportInterpreterResult({'bpnr': -1, 'pending': 1,
2697                                              'error': 'Pending interpreter breakpoint insertion failed.'}, args)
2698                return
2699        else:
2700            if not bp:
2701                self.reportInterpreterResult({'bpnr': -1, 'pending': 1,
2702                                              'warning': 'Direct interpreter breakpoint insertion failed.'}, args)
2703                self.createResolvePendingBreakpointsHookBreakpoint(args)
2704                return
2705        self.reportInterpreterResult({'bpnr': bp, 'pending': 0}, args)
2706
2707    def isInternalInterpreterFrame(self, functionName):
2708        if functionName is None:
2709            return False
2710        if functionName.startswith('qt_v4'):
2711            return True
2712        return functionName.startswith(self.qtNamespace() + 'QV4::')
2713
2714    # Hack to avoid QDate* dumper timeouts with GDB 7.4 on 32 bit
2715    # due to misaligned %ebx in SSE calls (qstring.cpp:findChar)
2716    def canCallLocale(self):
2717        return True
2718
2719    def isReportableInterpreterFrame(self, functionName):
2720        return functionName and functionName.find('QV4::Moth::VME::exec') >= 0
2721
2722    def extractInterpreterStack(self):
2723        return self.sendInterpreterRequest('backtrace', {'limit': 10})
2724
2725    def isInt(self, thing):
2726        if isinstance(thing, int):
2727            return True
2728        if sys.version_info[0] == 2:
2729            if isinstance(thing, long):
2730                return True
2731        return False
2732
2733    def putItems(self, count, generator, maxNumChild=10000):
2734        self.putItemCount(count)
2735        if self.isExpanded():
2736            with Children(self, count, maxNumChild=maxNumChild):
2737                for i, val in zip(self.childRange(), generator):
2738                    self.putSubItem(i, val)
2739
2740    def putItem(self, value):
2741        #with self.timer('putItem'):
2742        self.putItemX(value)
2743
2744    def putItemX(self, value):
2745        #DumperBase.warn('PUT ITEM: %s' % value.stringify())
2746
2747        typeobj = value.type  # unqualified()
2748        typeName = typeobj.name
2749
2750        self.addToCache(typeobj)  # Fill type cache
2751
2752        if not value.lIsInScope:
2753            self.putSpecialValue('optimizedout')
2754            #self.putType(typeobj)
2755            #self.putSpecialValue('outofscope')
2756            self.putNumChild(0)
2757            return
2758
2759        if not isinstance(value, self.Value):
2760            raise RuntimeError('WRONG TYPE IN putItem: %s' % type(self.Value))
2761
2762        # Try on possibly typedefed type first.
2763        if self.tryPutPrettyItem(typeName, value):
2764            if typeobj.code == TypeCode.Pointer:
2765                self.putOriginalAddress(value.address())
2766            else:
2767                self.putAddress(value.address())
2768            return
2769
2770        if typeobj.code == TypeCode.Typedef:
2771            #DumperBase.warn('TYPEDEF VALUE: %s' % value.stringify())
2772            self.putItem(value.detypedef())
2773            self.putBetterType(typeName)
2774            return
2775
2776        if typeobj.code == TypeCode.Pointer:
2777            self.putFormattedPointer(value)
2778            if value.summary and self.useFancy:
2779                self.putValue(self.hexencode(value.summary), 'utf8:1:0')
2780            return
2781
2782        self.putAddress(value.address())
2783
2784        if typeobj.code == TypeCode.Function:
2785            #DumperBase.warn('FUNCTION VALUE: %s' % value)
2786            self.putType(typeobj)
2787            self.putSymbolValue(value.pointer())
2788            self.putNumChild(0)
2789            return
2790
2791        if typeobj.code == TypeCode.Enum:
2792            #DumperBase.warn('ENUM VALUE: %s' % value.stringify())
2793            self.putType(typeobj.name)
2794            self.putValue(value.display())
2795            self.putNumChild(0)
2796            return
2797
2798        if typeobj.code == TypeCode.Array:
2799            #DumperBase.warn('ARRAY VALUE: %s' % value)
2800            self.putCStyleArray(value)
2801            return
2802
2803        if typeobj.code == TypeCode.Bitfield:
2804            #DumperBase.warn('BITFIELD VALUE: %s %d %s' % (value.name, value.lvalue, typeName))
2805            self.putNumChild(0)
2806            dd = typeobj.ltarget.typeData().enumDisplay
2807            self.putValue(str(value.lvalue) if dd is None else dd(
2808                value.lvalue, value.laddress, '%d'))
2809            self.putType(typeName)
2810            return
2811
2812        if typeobj.code == TypeCode.Integral:
2813            #DumperBase.warn('INTEGER: %s %s' % (value.name, value))
2814            val = value.value()
2815            self.putNumChild(0)
2816            self.putValue(val)
2817            self.putType(typeName)
2818            return
2819
2820        if typeobj.code == TypeCode.Float:
2821            #DumperBase.warn('FLOAT VALUE: %s' % value)
2822            self.putValue(value.value())
2823            self.putNumChild(0)
2824            self.putType(typeobj.name)
2825            return
2826
2827        if typeobj.code in (TypeCode.Reference, TypeCode.RValueReference):
2828            #DumperBase.warn('REFERENCE VALUE: %s' % value)
2829            val = value.dereference()
2830            if val.laddress != 0:
2831                self.putItem(val)
2832            else:
2833                self.putSpecialValue('nullreference')
2834            self.putBetterType(typeName)
2835            return
2836
2837        if typeobj.code == TypeCode.Complex:
2838            self.putType(typeobj)
2839            self.putValue(value.display())
2840            self.putNumChild(0)
2841            return
2842
2843        if typeobj.code == TypeCode.FortranString:
2844            self.putValue(self.hexencode(value.data()), 'latin1')
2845            self.putNumChild(0)
2846            self.putType(typeobj)
2847
2848        if typeName.endswith('[]'):
2849            # D arrays, gdc compiled.
2850            n = value['length']
2851            base = value['ptr']
2852            self.putType(typeName)
2853            self.putItemCount(n)
2854            if self.isExpanded():
2855                self.putArrayData(base.pointer(), n, base.type.target())
2856            return
2857
2858        #DumperBase.warn('SOME VALUE: %s' % value)
2859        #DumperBase.warn('GENERIC STRUCT: %s' % typeobj)
2860        #DumperBase.warn('INAME: %s ' % self.currentIName)
2861        #DumperBase.warn('INAMES: %s ' % self.expandedINames)
2862        #DumperBase.warn('EXPANDED: %s ' % (self.currentIName in self.expandedINames))
2863        self.putType(typeName)
2864
2865        if value.summary is not None and self.useFancy:
2866            self.putValue(self.hexencode(value.summary), 'utf8:1:0')
2867            self.putNumChild(0)
2868            return
2869
2870        self.putExpandable()
2871        self.putEmptyValue()
2872        #DumperBase.warn('STRUCT GUTS: %s  ADDRESS: 0x%x ' % (value.name, value.address()))
2873        if self.showQObjectNames:
2874            #with self.timer(self.currentIName):
2875            self.putQObjectNameValue(value)
2876        if self.isExpanded():
2877            if not self.isCli:
2878                self.putField('sortable', 1)
2879            with Children(self, 1, childType=None):
2880                self.putFields(value)
2881                if self.showQObjectNames:
2882                    self.tryPutQObjectGuts(value)
2883
2884    def symbolAddress(self, symbolName):
2885        res = self.parseAndEvaluate('(size_t)&' + symbolName)
2886        return None if res is None else res.pointer()
2887
2888    def qtHookDataSymbolName(self):
2889        return 'qtHookData'
2890
2891    def qtTypeInfoVersion(self):
2892        addr = self.symbolAddress(self.qtHookDataSymbolName())
2893        if addr:
2894            # Only available with Qt 5.3+
2895            (hookVersion, x, x, x, x, x, tiVersion) = self.split('ppppppp', addr)
2896            #DumperBase.warn('HOOK: %s TI: %s' % (hookVersion, tiVersion))
2897            if hookVersion >= 3:
2898                self.qtTypeInfoVersion = lambda: tiVersion
2899                return tiVersion
2900        return None
2901
2902    def qtDeclarativeHookDataSymbolName(self):
2903        return 'qtDeclarativeHookData'
2904
2905    def qtDeclarativeTypeInfoVersion(self):
2906        addr = self.symbolAddress(self.qtDeclarativeHookDataSymbolName())
2907        if addr:
2908            # Only available with Qt 5.6+
2909            (hookVersion, x, tiVersion) = self.split('ppp', addr)
2910            if hookVersion >= 1:
2911                self.qtTypeInfoVersion = lambda: tiVersion
2912                return tiVersion
2913        return None
2914
2915    def addToCache(self, typeobj):
2916        typename = typeobj.name
2917        if typename in self.typesReported:
2918            return
2919        self.typesReported[typename] = True
2920        self.typesToReport[typename] = typeobj
2921
2922    class Value():
2923        def __init__(self, dumper):
2924            self.dumper = dumper
2925            self.name = None
2926            self._type = None
2927            self.ldata = None        # Target address in case of references and pointers.
2928            self.laddress = None     # Own address.
2929            self.lvalue = None
2930            self.lIsInScope = True
2931            self.ldisplay = None
2932            self.summary = None      # Always hexencoded UTF-8.
2933            self.lbitpos = None
2934            self.lbitsize = None
2935            self.targetValue = None  # For references.
2936            self.isBaseClass = None
2937            self.nativeValue = None
2938            self.autoDerefCount = 0
2939
2940        def copy(self):
2941            val = self.dumper.Value(self.dumper)
2942            val.dumper = self.dumper
2943            val.name = self.name
2944            val._type = self._type
2945            val.ldata = self.ldata
2946            val.laddress = self.laddress
2947            val.lIsInScope = self.lIsInScope
2948            val.ldisplay = self.ldisplay
2949            val.summary = self.summary
2950            val.lbitpos = self.lbitpos
2951            val.lbitsize = self.lbitsize
2952            val.targetValue = self.targetValue
2953            val.nativeValue = self.nativeValue
2954            return val
2955
2956        @property
2957        def type(self):
2958            if self._type is None and self.nativeValue is not None:
2959                self._type = self.dumper.nativeValueType(self.nativeValue)
2960            return self._type
2961
2962        def check(self):
2963            if self.laddress is not None and not self.dumper.isInt(self.laddress):
2964                raise RuntimeError('INCONSISTENT ADDRESS: %s' % type(self.laddress))
2965            if self.type is not None and not isinstance(self.type, self.dumper.Type):
2966                raise RuntimeError('INCONSISTENT TYPE: %s' % type(self.type))
2967
2968        def __str__(self):
2969            #raise RuntimeError('Not implemented')
2970            return self.stringify()
2971
2972        def __int__(self):
2973            return self.integer()
2974
2975        def stringify(self):
2976            addr = 'None' if self.laddress is None else ('0x%x' % self.laddress)
2977            return "Value(name='%s',type=%s,bsize=%s,bpos=%s,data=%s,address=%s)" \
2978                % (self.name, self.type.name, self.lbitsize, self.lbitpos,
2979                   self.dumper.hexencode(self.ldata), addr)
2980
2981        def displayEnum(self, form='%d', bitsize=None):
2982            intval = self.integer(bitsize)
2983            dd = self.type.typeData().enumDisplay
2984            if dd is None:
2985                return str(intval)
2986            return dd(intval, self.laddress, form)
2987
2988        def display(self):
2989            if self.ldisplay is not None:
2990                return self.ldisplay
2991            simple = self.value()
2992            if simple is not None:
2993                return str(simple)
2994            #if self.ldata is not None:
2995            #    if sys.version_info[0] == 2 and isinstance(self.ldata, buffer):
2996            #        return bytes(self.ldata).encode('hex')
2997            #    return self.ldata.encode('hex')
2998            if self.laddress is not None:
2999                return 'value of type %s at address 0x%x' % (self.type.name, self.laddress)
3000            return '<unknown data>'
3001
3002        def pointer(self):
3003            if self.type.code == TypeCode.Typedef:
3004                return self.detypedef().pointer()
3005            return self.extractInteger(self.dumper.ptrSize() * 8, True)
3006
3007        def integer(self, bitsize=None):
3008            if self.type.code == TypeCode.Typedef:
3009                return self.detypedef().integer()
3010            elif isinstance(self.lvalue, int):
3011                return self.lvalue
3012            # Could be something like 'short unsigned int'
3013            unsigned = self.type.name == 'unsigned' \
3014                or self.type.name.startswith('unsigned ') \
3015                or self.type.name.find(' unsigned ') != -1
3016            if bitsize is None:
3017                bitsize = self.type.bitsize()
3018            return self.extractInteger(bitsize, unsigned)
3019
3020        def floatingPoint(self):
3021            if self.nativeValue is not None and not self.dumper.isCdb:
3022                return str(self.nativeValue)
3023            if self.type.code == TypeCode.Typedef:
3024                return self.detypedef().floatingPoint()
3025            if self.type.size() == 8:
3026                return self.extractSomething('d', 64)
3027            if self.type.size() == 4:
3028                return self.extractSomething('f', 32)
3029            # Fall back in case we don't have a nativeValue at hand.
3030            # FIXME: This assumes Intel's 80bit extended floats. Which might
3031            # be wrong.
3032            l, h = self.split('QQ')
3033            if True:  # 80 bit floats
3034                sign = (h >> 15) & 1
3035                exp = (h & 0x7fff)
3036                fraction = l
3037                bit63 = (l >> 63) & 1
3038                #DumperBase.warn("SIGN: %s  EXP: %s  H: 0x%x L: 0x%x" % (sign, exp, h, l))
3039                if exp == 0:
3040                    if bit63 == 0:
3041                        if l == 0:
3042                            res = '-0' if sign else '0'
3043                        else:
3044                            res = (-1)**sign * l * 2**(-16382)  # subnormal
3045                    else:
3046                        res = 'pseudodenormal'
3047                elif exp == 0x7fff:
3048                    res = 'special'
3049                else:
3050                    res = (-1)**sign * l * 2**(exp - 16383 - 63)
3051            else:  # 128 bits
3052                sign = h >> 63
3053                exp = (h >> 48) & 0x7fff
3054                fraction = h & (2**48 - 1)
3055                #DumperBase.warn("SIGN: %s  EXP: %s  FRAC: %s  H: 0x%x L: 0x%x" % (sign, exp, fraction, h, l))
3056                if exp == 0:
3057                    if fraction == 0:
3058                        res = -0.0 if sign else 0.0
3059                    else:
3060                        res = (-1)**sign * fraction / 2**48 * 2**(-62)  # subnormal
3061                elif exp == 0x7fff:
3062                    res = ('-inf' if sign else 'inf') if fraction == 0 else 'nan'
3063                else:
3064                    res = (-1)**sign * (1 + fraction / 2**48) * 2**(exp - 63)
3065            return res
3066
3067        def value(self):
3068            if self.type is not None:
3069                if self.type.code == TypeCode.Enum:
3070                    return self.displayEnum()
3071                if self.type.code == TypeCode.Typedef:
3072                    return self.detypedef().value()
3073                if self.type.code == TypeCode.Integral:
3074                    return self.integer()
3075                if self.type.code == TypeCode.Bitfield:
3076                    return self.integer()
3077                if self.type.code == TypeCode.Float:
3078                    return self.floatingPoint()
3079                if self.type.code == TypeCode.Pointer:
3080                    return self.pointer()
3081            return None
3082
3083        def extractPointer(self):
3084            return self.split('p')[0]
3085
3086        def hasMember(self, name):
3087            return self.findMemberByName(name) is not None
3088
3089        def findMemberByName(self, name):
3090            self.check()
3091            if self.type.code == TypeCode.Typedef:
3092                return self.findMemberByName(self.detypedef())
3093            if self.type.code in (
3094                    TypeCode.Pointer,
3095                    TypeCode.Reference,
3096                    TypeCode.RValueReference):
3097                res = self.dereference().findMemberByName(name)
3098                if res is not None:
3099                    return res
3100            if self.type.code == TypeCode.Struct:
3101                #DumperBase.warn('SEARCHING FOR MEMBER: %s IN %s' % (name, self.type.name))
3102                members = self.members(True)
3103                #DumperBase.warn('MEMBERS: %s' % members)
3104                for member in members:
3105                    #DumperBase.warn('CHECKING FIELD %s' % member.name)
3106                    if member.type.code == TypeCode.Typedef:
3107                        member = member.detypedef()
3108                    if member.name == name:
3109                        return member
3110                for member in members:
3111                    if member.type.code == TypeCode.Typedef:
3112                        member = member.detypedef()
3113                    if member.name == name:  # Could be base class.
3114                        return member
3115                    if member.type.code == TypeCode.Struct:
3116                        res = member.findMemberByName(name)
3117                        if res is not None:
3118                            return res
3119            return None
3120
3121        def __getitem__(self, index):
3122            #DumperBase.warn('GET ITEM %s %s' % (self, index))
3123            self.check()
3124            if self.type.code == TypeCode.Typedef:
3125                #DumperBase.warn('GET ITEM STRIP TYPEDEFS TO %s' % self.type.ltarget)
3126                return self.cast(self.type.ltarget).__getitem__(index)
3127            if isinstance(index, str):
3128                if self.type.code == TypeCode.Pointer:
3129                    #DumperBase.warn('GET ITEM %s DEREFERENCE TO %s' % (self, self.dereference()))
3130                    return self.dereference().__getitem__(index)
3131                res = self.findMemberByName(index)
3132                if res is None:
3133                    raise RuntimeError('No member named %s in type %s'
3134                                       % (index, self.type.name))
3135                return res
3136            elif isinstance(index, self.dumper.Field):
3137                field = index
3138            elif self.dumper.isInt(index):
3139                if self.type.code == TypeCode.Array:
3140                    addr = self.laddress + int(index) * self.type.ltarget.size()
3141                    return self.dumper.createValue(addr, self.type.ltarget)
3142                if self.type.code == TypeCode.Pointer:
3143                    addr = self.pointer() + int(index) * self.type.ltarget.size()
3144                    return self.dumper.createValue(addr, self.type.ltarget)
3145                return self.members(False)[index]
3146            else:
3147                raise RuntimeError('BAD INDEX TYPE %s' % type(index))
3148            field.check()
3149
3150            #DumperBase.warn('EXTRACT FIELD: %s, BASE 0x%x' % (field, self.address()))
3151            if self.type.code == TypeCode.Pointer:
3152                #DumperBase.warn('IS TYPEDEFED POINTER!')
3153                res = self.dereference()
3154                #DumperBase.warn('WAS POINTER: %s' % res)
3155
3156            return field.extract(self)
3157
3158        def extractField(self, field):
3159            if not isinstance(field, self.dumper.Field):
3160                raise RuntimeError('BAD INDEX TYPE %s' % type(field))
3161
3162            if field.extractor is not None:
3163                val = field.extractor(self)
3164                if val is not None:
3165                    #DumperBase.warn('EXTRACTOR SUCCEEDED: %s ' % val)
3166                    return val
3167
3168            if self.type.code == TypeCode.Typedef:
3169                return self.cast(self.type.ltarget).extractField(field)
3170            if self.type.code in (TypeCode.Reference, TypeCode.RValueReference):
3171                return self.dereference().extractField(field)
3172            #DumperBase.warn('FIELD: %s ' % field)
3173            val = self.dumper.Value(self.dumper)
3174            val.name = field.name
3175            val.isBaseClass = field.isBase
3176            val._type = field.fieldType()
3177
3178            if field.isArtificial:
3179                if self.laddress is not None:
3180                    val.laddress = self.laddress
3181                if self.ldata is not None:
3182                    val.ldata = self.ldata
3183                return val
3184
3185            fieldBitsize = field.bitsize
3186            fieldSize = (fieldBitsize + 7) // 8
3187            fieldBitpos = field.bitpos
3188            fieldOffset = fieldBitpos // 8
3189            fieldType = field.fieldType()
3190
3191            if fieldType.code == TypeCode.Bitfield:
3192                fieldBitpos -= fieldOffset * 8
3193                ldata = self.data()
3194                data = 0
3195                for i in range(fieldSize):
3196                    data = data << 8
3197                    if self.dumper.isBigEndian:
3198                        lbyte = ldata[i]
3199                    else:
3200                        lbyte = ldata[fieldOffset + fieldSize - 1 - i]
3201                    if sys.version_info[0] >= 3:
3202                        data += lbyte
3203                    else:
3204                        data += ord(lbyte)
3205                data = data >> fieldBitpos
3206                data = data & ((1 << fieldBitsize) - 1)
3207                val.lvalue = data
3208                val.laddress = None
3209                return val
3210
3211            if self.laddress is not None:
3212                val.laddress = self.laddress + fieldOffset
3213            elif self.ldata is not None:
3214                val.ldata = self.ldata[fieldOffset:fieldOffset + fieldSize]
3215            else:
3216                self.dumper.check(False)
3217
3218            if fieldType.code in (TypeCode.Reference, TypeCode.RValueReference):
3219                if val.laddress is not None:
3220                    val = self.dumper.createReferenceValue(val.laddress, fieldType.ltarget)
3221                    val.name = field.name
3222
3223            #DumperBase.warn('GOT VAL %s FOR FIELD %s' % (val, field))
3224            val.lbitsize = fieldBitsize
3225            val.check()
3226            return val
3227
3228        # This is the generic version for synthetic values.
3229        # The native backends replace it in their fromNativeValue()
3230        # implementations.
3231        def members(self, includeBases):
3232            #DumperBase.warn("LISTING MEMBERS OF %s" % self)
3233            if self.type.code == TypeCode.Typedef:
3234                return self.detypedef().members(includeBases)
3235
3236            tdata = self.type.typeData()
3237            #if isinstance(tdata.lfields, list):
3238            #    return tdata.lfields
3239
3240            fields = []
3241            if tdata.lfields is not None:
3242                if isinstance(tdata.lfields, list):
3243                    fields = tdata.lfields
3244                else:
3245                    fields = list(tdata.lfields(self))
3246
3247            #DumperBase.warn("FIELDS: %s" % fields)
3248            res = []
3249            for field in fields:
3250                if isinstance(field, self.dumper.Value):
3251                    #DumperBase.warn("USING VALUE DIRECTLY %s" % field.name)
3252                    res.append(field)
3253                    continue
3254                if field.isBase and not includeBases:
3255                    #DumperBase.warn("DROPPING BASE %s" % field.name)
3256                    continue
3257                res.append(self.extractField(field))
3258            #DumperBase.warn("GOT MEMBERS: %s" % res)
3259            return res
3260
3261        def __add__(self, other):
3262            self.check()
3263            if self.dumper.isInt(other):
3264                stripped = self.type.stripTypedefs()
3265                if stripped.code == TypeCode.Pointer:
3266                    address = self.pointer() + stripped.dereference().size() * other
3267                    val = self.dumper.Value(self.dumper)
3268                    val.laddress = None
3269                    val.ldata = bytes(struct.pack(self.dumper.packCode + 'Q', address))
3270                    val._type = self._type
3271                    return val
3272            raise RuntimeError('BAD DATA TO ADD TO: %s %s' % (self.type, other))
3273
3274        def __sub__(self, other):
3275            self.check()
3276            if self.type.name == other.type.name:
3277                stripped = self.type.stripTypedefs()
3278                if stripped.code == TypeCode.Pointer:
3279                    return (self.pointer() - other.pointer()) // stripped.dereference().size()
3280            raise RuntimeError('BAD DATA TO SUB TO: %s %s' % (self.type, other))
3281
3282        def dereference(self):
3283            self.check()
3284            if self.type.code == TypeCode.Typedef:
3285                return self.detypedef().dereference()
3286            val = self.dumper.Value(self.dumper)
3287            if self.type.code in (TypeCode.Reference, TypeCode.RValueReference):
3288                val.summary = self.summary
3289                if self.nativeValue is None:
3290                    val.laddress = self.pointer()
3291                    if val.laddress is None and self.laddress is not None:
3292                        val.laddress = self.laddress
3293                    val._type = self.type.dereference()
3294                    if self.dumper.useDynamicType:
3295                        val._type = self.dumper.nativeDynamicType(val.laddress, val.type)
3296                else:
3297                    val = self.dumper.nativeValueDereferenceReference(self)
3298            elif self.type.code == TypeCode.Pointer:
3299                if self.nativeValue is None:
3300                    val.laddress = self.pointer()
3301                    val._type = self.type.dereference()
3302                    if self.dumper.useDynamicType:
3303                        val._type = self.dumper.nativeDynamicType(val.laddress, val.type)
3304                else:
3305                    val = self.dumper.nativeValueDereferencePointer(self)
3306            else:
3307                raise RuntimeError("WRONG: %s" % self.type.code)
3308            #DumperBase.warn("DEREFERENCING FROM: %s" % self)
3309            #DumperBase.warn("DEREFERENCING TO: %s" % val)
3310            #dynTypeName = val.type.dynamicTypeName(val.laddress)
3311            #if dynTypeName is not None:
3312            #    val._type = self.dumper.createType(dynTypeName)
3313            return val
3314
3315        def detypedef(self):
3316            self.check()
3317            if self.type.code != TypeCode.Typedef:
3318                raise RuntimeError("WRONG")
3319            val = self.copy()
3320            val._type = self.type.ltarget
3321            #DumperBase.warn("DETYPEDEF FROM: %s" % self)
3322            #DumperBase.warn("DETYPEDEF TO: %s" % val)
3323            return val
3324
3325        def extend(self, size):
3326            if self.type.size() < size:
3327                val = self.dumper.Value(self.dumper)
3328                val.laddress = None
3329                val.ldata = self.zeroExtend(self.ldata)
3330                return val
3331            if self.type.size() == size:
3332                return self
3333            raise RuntimeError('NOT IMPLEMENTED')
3334
3335        def zeroExtend(self, data, size):
3336            ext = '\0' * (size - len(data))
3337            if sys.version_info[0] == 3:
3338                pad = bytes(ext, encoding='latin1')
3339            else:
3340                pad = bytes(ext)
3341            return pad + data if self.dumper.isBigEndian else data + pad
3342
3343        def cast(self, typish):
3344            self.check()
3345            val = self.dumper.Value(self.dumper)
3346            val.laddress = self.laddress
3347            val.lbitsize = self.lbitsize
3348            val.ldata = self.ldata
3349            val._type = self.dumper.createType(typish)
3350            return val
3351
3352        def address(self):
3353            self.check()
3354            return self.laddress
3355
3356        def data(self, size=None):
3357            self.check()
3358            if self.ldata is not None:
3359                if len(self.ldata) > 0:
3360                    if size is None:
3361                        return self.ldata
3362                    if size == len(self.ldata):
3363                        return self.ldata
3364                    if size < len(self.ldata):
3365                        return self.ldata[:size]
3366                    #raise RuntimeError('ZERO-EXTENDING  DATA TO %s BYTES: %s' % (size, self))
3367                    return self.zeroExtend(self.ldata, size)
3368            if self.laddress is not None:
3369                if size is None:
3370                    size = self.type.size()
3371                res = self.dumper.readRawMemory(self.laddress, size)
3372                if len(res) > 0:
3373                    return res
3374                raise RuntimeError('CANNOT CONVERT ADDRESS TO BYTES: %s' % self)
3375            raise RuntimeError('CANNOT CONVERT TO BYTES: %s' % self)
3376
3377        def extractInteger(self, bitsize, unsigned):
3378            #with self.dumper.timer('extractInt'):
3379            self.check()
3380            if bitsize > 32:
3381                size = 8
3382                code = 'Q' if unsigned else 'q'
3383            elif bitsize > 16:
3384                size = 4
3385                code = 'I' if unsigned else 'i'
3386            elif bitsize > 8:
3387                size = 2
3388                code = 'H' if unsigned else 'h'
3389            else:
3390                size = 1
3391                code = 'B' if unsigned else 'b'
3392            rawBytes = self.data(size)
3393            res = struct.unpack_from(self.dumper.packCode + code, rawBytes, 0)[0]
3394            #DumperBase.warn('Extract: Code: %s Bytes: %s Bitsize: %s Size: %s'
3395            #    % (self.dumper.packCode + code, self.dumper.hexencode(rawBytes), bitsize, size))
3396            return res
3397
3398        def extractSomething(self, code, bitsize):
3399            #with self.dumper.timer('extractSomething'):
3400            self.check()
3401            size = (bitsize + 7) >> 3
3402            rawBytes = self.data(size)
3403            res = struct.unpack_from(self.dumper.packCode + code, rawBytes, 0)[0]
3404            return res
3405
3406        def to(self, pattern):
3407            return self.split(pattern)[0]
3408
3409        def split(self, pattern):
3410            #with self.dumper.timer('split'):
3411            #DumperBase.warn('EXTRACT STRUCT FROM: %s' % self.type)
3412            (pp, size, fields) = self.dumper.describeStruct(pattern)
3413            #DumperBase.warn('SIZE: %s ' % size)
3414            result = struct.unpack_from(self.dumper.packCode + pp, self.data(size))
3415
3416            def structFixer(field, thing):
3417                #DumperBase.warn('STRUCT MEMBER: %s' % type(thing))
3418                if field.isStruct:
3419                    #if field.type != field.fieldType():
3420                    #    raise RuntimeError('DO NOT SIMPLIFY')
3421                    #DumperBase.warn('FIELD POS: %s' % field.type.stringify())
3422                    #DumperBase.warn('FIELD TYE: %s' % field.fieldType().stringify())
3423                    res = self.dumper.createValue(thing, field.fieldType())
3424                    #DumperBase.warn('RES TYPE: %s' % res.type)
3425                    if self.laddress is not None:
3426                        res.laddress = self.laddress + field.offset()
3427                    return res
3428                return thing
3429            if len(fields) != len(result):
3430                raise RuntimeError('STRUCT ERROR: %s %s' % (fields, result))
3431            return tuple(map(structFixer, fields, result))
3432
3433    def checkPointer(self, p, align=1):
3434        ptr = p if self.isInt(p) else p.pointer()
3435        self.readRawMemory(ptr, 1)
3436
3437    def type(self, typeId):
3438        return self.typeData.get(typeId)
3439
3440    def splitArrayType(self, type_name):
3441        #  "foo[2][3][4]" ->  ("foo", "[3][4]", 2)
3442        pos1 = len(type_name)
3443        # In case there are more dimensions we need the inner one.
3444        while True:
3445            pos1 = type_name.rfind('[', 0, pos1 - 1)
3446            pos2 = type_name.find(']', pos1)
3447            if type_name[pos1 - 1] != ']':
3448                break
3449
3450        item_count = type_name[pos1 + 1:pos2]
3451        return (type_name[0:pos1].strip(), type_name[pos2 + 1:].strip(), int(item_count))
3452
3453    def registerType(self, typeId, tdata):
3454        #DumperBase.warn('REGISTER TYPE: %s' % typeId)
3455        self.typeData[typeId] = tdata
3456        #typeId = typeId.replace(' ', '')
3457        #self.typeData[typeId] = tdata
3458        #DumperBase.warn('REGISTERED: %s' % self.typeData)
3459
3460    def registerTypeAlias(self, existingTypeId, aliasId):
3461        #DumperBase.warn('REGISTER ALIAS %s FOR %s' % (aliasId, existingTypeId))
3462        self.typeData[aliasId] = self.typeData[existingTypeId]
3463
3464    class TypeData():
3465        def __init__(self, dumper):
3466            self.dumper = dumper
3467            self.lfields = None  # None or Value -> list of member Values
3468            self.lalignment = None  # Function returning alignment of this struct
3469            self.lbitsize = None
3470            self.ltarget = None  # Inner type for arrays
3471            self.templateArguments = []
3472            self.code = None
3473            self.name = None
3474            self.typeId = None
3475            self.enumDisplay = None
3476            self.moduleName = None
3477
3478        def copy(self):
3479            tdata = self.dumper.TypeData(self.dumper)
3480            tdata.dumper = self.dumper
3481            tdata.lfields = self.lfields
3482            tdata.lalignment = self.lalignment
3483            tdata.lbitsize = self.lbitsize
3484            tdata.ltarget = self.ltarget
3485            tdata.templateArguments = self.templateArguments
3486            tdata.code = self.code
3487            tdata.name = self.name
3488            tdata.typeId = self.typeId
3489            tdata.enumDisplay = self.enumDisplay
3490            tdata.moduleName = self.moduleName
3491            return tdata
3492
3493    class Type():
3494        def __init__(self, dumper, typeId):
3495            self.typeId = typeId
3496            self.dumper = dumper
3497
3498        def __str__(self):
3499            #return self.typeId
3500            return self.stringify()
3501
3502        def typeData(self):
3503            tdata = self.dumper.typeData.get(self.typeId, None)
3504            if tdata is not None:
3505                #DumperBase.warn('USING : %s' % self.typeId)
3506                return tdata
3507            typeId = self.typeId.replace(' ', '')
3508            if tdata is not None:
3509                #DumperBase.warn('USING FALLBACK : %s' % self.typeId)
3510                return tdata
3511            #DumperBase.warn('EXPANDING LAZILY: %s' % self.typeId)
3512            self.dumper.lookupType(self.typeId)
3513            return self.dumper.typeData.get(self.typeId)
3514
3515        @property
3516        def name(self):
3517            tdata = self.dumper.typeData.get(self.typeId)
3518            if tdata is None:
3519                return self.typeId
3520            return tdata.name
3521
3522        @property
3523        def code(self):
3524            return self.typeData().code
3525
3526        @property
3527        def lbitsize(self):
3528            return self.typeData().lbitsize
3529
3530        @property
3531        def lbitpos(self):
3532            return self.typeData().lbitpos
3533
3534        @property
3535        def ltarget(self):
3536            return self.typeData().ltarget
3537
3538        @property
3539        def moduleName(self):
3540            return self.typeData().moduleName
3541
3542        def stringify(self):
3543            tdata = self.typeData()
3544            if tdata is None:
3545                return 'Type(id="%s")' % self.typeId
3546            return 'Type(name="%s",bsize=%s,code=%s)' \
3547                % (tdata.name, tdata.lbitsize, tdata.code)
3548
3549        def __getitem__(self, index):
3550            if self.dumper.isInt(index):
3551                return self.templateArgument(index)
3552            raise RuntimeError('CANNOT INDEX TYPE')
3553
3554        def dynamicTypeName(self, address):
3555            tdata = self.typeData()
3556            if tdata is None:
3557                return None
3558            if tdata.code != TypeCode.Struct:
3559                return None
3560            try:
3561                vtbl = self.dumper.extractPointer(address)
3562            except:
3563                return None
3564            #DumperBase.warn('VTBL: 0x%x' % vtbl)
3565            if not self.dumper.couldBePointer(vtbl):
3566                return None
3567            return self.dumper.nativeDynamicTypeName(address, self)
3568
3569        def dynamicType(self, address):
3570            # FIXME: That buys some performance at the cost of a fail
3571            # of Gdb13393 dumper test.
3572            #return self
3573            #with self.dumper.timer('dynamicType %s 0x%s' % (self.name, address)):
3574            dynTypeName = self.dynamicTypeName(address)
3575            if dynTypeName is not None:
3576                return self.dumper.createType(dynTypeName)
3577            return self
3578
3579        def check(self):
3580            tdata = self.typeData()
3581            if tdata is None:
3582                raise RuntimeError('TYPE WITHOUT DATA: %s ALL: %s' %
3583                                   (self.typeId, self.dumper.typeData.keys()))
3584            if tdata.name is None:
3585                raise RuntimeError('TYPE WITHOUT NAME: %s' % self.typeId)
3586
3587        def dereference(self):
3588            if self.code == TypeCode.Typedef:
3589                return self.ltarget.dereference()
3590            self.check()
3591            return self.ltarget
3592
3593        def unqualified(self):
3594            return self
3595
3596        def templateArguments(self):
3597            tdata = self.typeData()
3598            if tdata is None:
3599                return self.dumper.listTemplateParameters(self.typeId)
3600            return tdata.templateArguments
3601
3602        def templateArgument(self, position):
3603            tdata = self.typeData()
3604            #DumperBase.warn('TDATA: %s' % tdata)
3605            #DumperBase.warn('ID: %s' % self.typeId)
3606            if tdata is None:
3607                # Native lookups didn't help. Happens for 'wrong' placement of 'const'
3608                # etc. with LLDB. But not all is lost:
3609                ta = self.dumper.listTemplateParameters(self.typeId)
3610                #DumperBase.warn('MANUAL: %s' % ta)
3611                res = ta[position]
3612                #DumperBase.warn('RES: %s' % res.typeId)
3613                return res
3614            #DumperBase.warn('TA: %s %s' % (position, self.typeId))
3615            #DumperBase.warn('ARGS: %s' % tdata.templateArguments)
3616            return tdata.templateArguments[position]
3617
3618        def simpleEncoding(self):
3619            res = {
3620                'bool': 'int:1',
3621                'char': 'int:1',
3622                'signed char': 'int:1',
3623                'unsigned char': 'uint:1',
3624                'short': 'int:2',
3625                'unsigned short': 'uint:2',
3626                'int': 'int:4',
3627                'unsigned int': 'uint:4',
3628                'long long': 'int:8',
3629                'unsigned long long': 'uint:8',
3630                'float': 'float:4',
3631                'double': 'float:8',
3632                'QChar': 'uint:2'
3633            }.get(self.name, None)
3634            return res
3635
3636        def isSimpleType(self):
3637            return self.code in (TypeCode.Integral, TypeCode.Float, TypeCode.Enum)
3638
3639        def alignment(self):
3640            tdata = self.typeData()
3641            if tdata.code == TypeCode.Typedef:
3642                return tdata.ltarget.alignment()
3643            if tdata.code in (TypeCode.Integral, TypeCode.Float, TypeCode.Enum):
3644                if tdata.name in ('double', 'long long', 'unsigned long long'):
3645                    # Crude approximation.
3646                    return 8 if self.dumper.isWindowsTarget() else self.dumper.ptrSize()
3647                return self.size()
3648            if tdata.code in (TypeCode.Pointer, TypeCode.Reference, TypeCode.RValueReference):
3649                return self.dumper.ptrSize()
3650            if tdata.lalignment is not None:
3651                #if isinstance(tdata.lalignment, function): # Does not work that way.
3652                if hasattr(tdata.lalignment, '__call__'):
3653                    return tdata.lalignment()
3654                return tdata.lalignment
3655            return 1
3656
3657        def pointer(self):
3658            return self.dumper.createPointerType(self)
3659
3660        def target(self):
3661            return self.typeData().ltarget
3662
3663        def stripTypedefs(self):
3664            if isinstance(self, self.dumper.Type) and self.code != TypeCode.Typedef:
3665                #DumperBase.warn('NO TYPEDEF: %s' % self)
3666                return self
3667            return self.ltarget
3668
3669        def size(self):
3670            bs = self.bitsize()
3671            if bs % 8 != 0:
3672                DumperBase.warn('ODD SIZE: %s' % self)
3673            return (7 + bs) >> 3
3674
3675        def bitsize(self):
3676            if self.lbitsize is not None:
3677                return self.lbitsize
3678            raise RuntimeError('DONT KNOW SIZE: %s' % self)
3679
3680        def isMovableType(self):
3681            if self.code in (TypeCode.Pointer, TypeCode.Integral, TypeCode.Float):
3682                return True
3683            strippedName = self.dumper.stripNamespaceFromType(self.name)
3684            if strippedName in (
3685                    'QBrush', 'QBitArray', 'QByteArray', 'QCustomTypeInfo',
3686                    'QChar', 'QDate', 'QDateTime', 'QFileInfo', 'QFixed',
3687                    'QFixedPoint', 'QFixedSize', 'QHashDummyValue', 'QIcon',
3688                    'QImage', 'QLine', 'QLineF', 'QLatin1Char', 'QLocale',
3689                    'QMatrix', 'QModelIndex', 'QPoint', 'QPointF', 'QPen',
3690                    'QPersistentModelIndex', 'QResourceRoot', 'QRect', 'QRectF',
3691                    'QRegExp', 'QSize', 'QSizeF', 'QString', 'QTime', 'QTextBlock',
3692                    'QUrl', 'QVariant',
3693                    'QXmlStreamAttribute', 'QXmlStreamNamespaceDeclaration',
3694                    'QXmlStreamNotationDeclaration', 'QXmlStreamEntityDeclaration'
3695            ):
3696                return True
3697            if strippedName == 'QStringList':
3698                return self.dumper.qtVersion() >= 0x050000
3699            if strippedName == 'QList':
3700                return self.dumper.qtVersion() >= 0x050600
3701            return False
3702
3703    class Field(collections.namedtuple('Field',
3704                                       ['dumper', 'name', 'type', 'bitsize', 'bitpos',
3705                                        'extractor', 'isBase', 'isStruct', 'isArtificial'])):
3706
3707        def __new__(cls, dumper, name=None, type=None, bitsize=None, bitpos=None,
3708                    extractor=None, isBase=False, isStruct=False, isArtificial=False):
3709            return super(DumperBase.Field, cls).__new__(
3710                cls, dumper, name, type, bitsize, bitpos,
3711                extractor, isBase, isStruct, isArtificial)
3712
3713        __slots__ = ()
3714
3715        def __str__(self):
3716            return self.stringify()
3717
3718        def stringify(self):
3719            #return 'Field(name="%s")' % self.name
3720            typename = None if self.type is None else self.type.stringify()
3721            return 'Field(name="%s",type=%s,bitpos=%s,bitsize=%s)' \
3722                % (self.name, typename, self.bitpos, self.bitsize)
3723
3724        def check(self):
3725            pass
3726
3727        def size(self):
3728            return self.bitsize() // 8
3729
3730        def offset(self):
3731            return self.bitpos // 8
3732
3733        def fieldType(self):
3734            if self.type is not None:
3735                return self.type
3736            raise RuntimeError('CANT GET FIELD TYPE FOR %s' % self)
3737            return None
3738
3739    def ptrCode(self):
3740        return 'I' if self.ptrSize() == 4 else 'Q'
3741
3742    def toPointerData(self, address):
3743        if not self.isInt(address):
3744            raise RuntimeError('wrong')
3745        return bytes(struct.pack(self.packCode + self.ptrCode(), address))
3746
3747    def fromPointerData(self, bytes_value):
3748        return struct.unpack(self.packCode + self.ptrCode(), bytes_value)
3749
3750    def createPointerValue(self, targetAddress, targetTypish):
3751        if not isinstance(targetTypish, self.Type) and not isinstance(targetTypish, str):
3752            raise RuntimeError('Expected type in createPointerValue(), got %s'
3753                               % type(targetTypish))
3754        if not self.isInt(targetAddress):
3755            raise RuntimeError('Expected integral address value in createPointerValue(), got %s'
3756                               % type(targetTypish))
3757        val = self.Value(self)
3758        val.ldata = self.toPointerData(targetAddress)
3759        targetType = self.createType(targetTypish)
3760        if self.useDynamicType:
3761            targetType = targetType.dynamicType(targetAddress)
3762        val._type = self.createPointerType(targetType)
3763        return val
3764
3765    def createReferenceValue(self, targetAddress, targetType):
3766        if not isinstance(targetType, self.Type):
3767            raise RuntimeError('Expected type in createReferenceValue(), got %s'
3768                               % type(targetType))
3769        if not self.isInt(targetAddress):
3770            raise RuntimeError('Expected integral address value in createReferenceValue(), got %s'
3771                               % type(targetType))
3772        val = self.Value(self)
3773        val.ldata = self.toPointerData(targetAddress)
3774        if self.useDynamicType:
3775            targetType = targetType.dynamicType(targetAddress)
3776        val._type = self.createReferenceType(targetType)
3777        return val
3778
3779    def createPointerType(self, targetType):
3780        if not isinstance(targetType, self.Type):
3781            raise RuntimeError('Expected type in createPointerType(), got %s'
3782                               % type(targetType))
3783        typeId = targetType.typeId + ' *'
3784        tdata = self.TypeData(self)
3785        tdata.name = targetType.name + '*'
3786        tdata.typeId = typeId
3787        tdata.lbitsize = 8 * self.ptrSize()
3788        tdata.code = TypeCode.Pointer
3789        tdata.ltarget = targetType
3790        self.registerType(typeId, tdata)
3791        return self.Type(self, typeId)
3792
3793    def createReferenceType(self, targetType):
3794        if not isinstance(targetType, self.Type):
3795            raise RuntimeError('Expected type in createReferenceType(), got %s'
3796                               % type(targetType))
3797        typeId = targetType.typeId + ' &'
3798        tdata = self.TypeData(self)
3799        tdata.name = targetType.name + ' &'
3800        tdata.typeId = typeId
3801        tdata.code = TypeCode.Reference
3802        tdata.ltarget = targetType
3803        tdata.lbitsize = 8 * self.ptrSize()  # Needed for Gdb13393 test.
3804        #tdata.lbitsize = None
3805        self.registerType(typeId, tdata)
3806        return self.Type(self, typeId)
3807
3808    def createRValueReferenceType(self, targetType):
3809        if not isinstance(targetType, self.Type):
3810            raise RuntimeError('Expected type in createRValueReferenceType(), got %s'
3811                               % type(targetType))
3812        typeId = targetType.typeId + ' &&'
3813        tdata = self.TypeData(self)
3814        tdata.name = targetType.name + ' &&'
3815        tdata.typeId = typeId
3816        tdata.code = TypeCode.RValueReference
3817        tdata.ltarget = targetType
3818        tdata.lbitsize = None
3819        self.registerType(typeId, tdata)
3820        return self.Type(self, typeId)
3821
3822    def createArrayType(self, targetType, count):
3823        if not isinstance(targetType, self.Type):
3824            raise RuntimeError('Expected type in createArrayType(), got %s'
3825                               % type(targetType))
3826        targetTypeId = targetType.typeId
3827
3828        if targetTypeId.endswith(']'):
3829            (prefix, suffix, inner_count) = self.splitArrayType(targetTypeId)
3830            type_id = '%s[%d][%d]%s' % (prefix, count, inner_count, suffix)
3831            type_name = type_id
3832        else:
3833            type_id = '%s[%d]' % (targetTypeId, count)
3834            type_name = '%s[%d]' % (targetType.name, count)
3835
3836        tdata = self.TypeData(self)
3837        tdata.name = type_name
3838        tdata.typeId = type_id
3839        tdata.code = TypeCode.Array
3840        tdata.ltarget = targetType
3841        tdata.lbitsize = targetType.lbitsize * count
3842        self.registerType(type_id, tdata)
3843        return self.Type(self, type_id)
3844
3845    def createBitfieldType(self, targetType, bitsize):
3846        if not isinstance(targetType, self.Type):
3847            raise RuntimeError('Expected type in createBitfieldType(), got %s'
3848                               % type(targetType))
3849        typeId = '%s:%d' % (targetType.typeId, bitsize)
3850        tdata = self.TypeData(self)
3851        tdata.name = '%s : %d' % (targetType.typeId, bitsize)
3852        tdata.typeId = typeId
3853        tdata.code = TypeCode.Bitfield
3854        tdata.ltarget = targetType
3855        tdata.lbitsize = bitsize
3856        self.registerType(typeId, tdata)
3857        return self.Type(self, typeId)
3858
3859    def createTypedefedType(self, targetType, typeName, typeId=None):
3860        if typeId is None:
3861            typeId = typeName
3862        if not isinstance(targetType, self.Type):
3863            raise RuntimeError('Expected type in createTypedefType(), got %s'
3864                               % type(targetType))
3865        # Happens for C-style struct in GDB: typedef { int x; } struct S1;
3866        if targetType.typeId == typeId:
3867            return targetType
3868        tdata = self.TypeData(self)
3869        tdata.name = typeName
3870        tdata.typeId = typeId
3871        tdata.code = TypeCode.Typedef
3872        tdata.ltarget = targetType
3873        tdata.lbitsize = targetType.lbitsize
3874        #tdata.lfields = targetType.lfields
3875        tdata.lbitsize = targetType.lbitsize
3876        self.registerType(typeId, tdata)
3877        return self.Type(self, typeId)
3878
3879    def knownArrayTypeSize(self):
3880        return 3 * self.ptrSize() if self.qtVersion() >= 0x060000 else self.ptrSize()
3881
3882    def knownTypeSize(self, typish):
3883        if typish[0] == 'Q':
3884            if typish.startswith('QList<') or typish.startswith('QVector<'):
3885                return self.knownArrayTypeSize()
3886            if typish == 'QObject':
3887                return 2 * self.ptrSize()
3888            if typish == 'QStandardItemData':
3889                return 4 * self.ptrSize() if self.qtVersion() >= 0x060000 else 2 * self.ptrSize()
3890            if typish == 'Qt::ItemDataRole':
3891                return 4
3892            if typish == 'QChar':
3893                return 2
3894        if typish in ('quint32', 'qint32'):
3895            return 4
3896        return None
3897
3898    def createType(self, typish, size=None):
3899        if isinstance(typish, self.Type):
3900            #typish.check()
3901            if hasattr(typish, 'lbitsize') and typish.lbitsize is not None and typish.lbitsize > 0:
3902                return typish
3903            # Size 0 is sometimes reported by GDB but doesn't help at all.
3904            # Force using the fallback:
3905            typish = typish.name
3906
3907        if isinstance(typish, str):
3908            ns = self.qtNamespace()
3909            typish = typish.replace('@', ns)
3910            if typish.startswith(ns):
3911                if size is None:
3912                    size = self.knownTypeSize(typish[len(ns):])
3913            else:
3914                if size is None:
3915                    size = self.knownTypeSize(typish)
3916                if size is not None:
3917                    typish = ns + typish
3918
3919            tdata = self.typeData.get(typish, None)
3920            if tdata is not None:
3921                if tdata.lbitsize is not None:
3922                    if tdata.lbitsize > 0:
3923                        return self.Type(self, typish)
3924
3925            knownType = self.lookupType(typish)
3926            #DumperBase.warn('KNOWN: %s' % knownType)
3927            if knownType is not None:
3928                #DumperBase.warn('USE FROM NATIVE')
3929                return knownType
3930
3931            #DumperBase.warn('FAKING: %s SIZE: %s' % (typish, size))
3932            tdata = self.TypeData(self)
3933            tdata.name = typish
3934            tdata.typeId = typish
3935            tdata.templateArguments = self.listTemplateParameters(typish)
3936            if size is not None:
3937                tdata.lbitsize = 8 * size
3938            if typish.endswith('*'):
3939                tdata.code = TypeCode.Pointer
3940                tdata.lbitsize = 8 * self.ptrSize()
3941                tdata.ltarget = self.createType(typish[:-1].strip())
3942
3943            self.registerType(typish, tdata)
3944            typeobj = self.Type(self, typish)
3945            #DumperBase.warn('CREATE TYPE: %s' % typeobj.stringify())
3946            typeobj.check()
3947            return typeobj
3948        raise RuntimeError('NEED TYPE, NOT %s' % type(typish))
3949
3950    def createValue(self, datish, typish):
3951        val = self.Value(self)
3952        val._type = self.createType(typish)
3953        if self.isInt(datish):  # Used as address.
3954            #DumperBase.warn('CREATING %s AT 0x%x' % (val.type.name, datish))
3955            val.laddress = datish
3956            if self.useDynamicType:
3957                val._type = val.type.dynamicType(datish)
3958            return val
3959        if isinstance(datish, bytes):
3960            #DumperBase.warn('CREATING %s WITH DATA %s' % (val.type.name, self.hexencode(datish)))
3961            val.ldata = datish
3962            val.check()
3963            return val
3964        raise RuntimeError('EXPECTING ADDRESS OR BYTES, GOT %s' % type(datish))
3965
3966    def createProxyValue(self, proxy_data, type_name):
3967        tdata = self.TypeData(self)
3968        tdata.name = type_name
3969        tdata.typeId = type_name
3970        tdata.code = TypeCode.Struct
3971        self.registerType(type_name, tdata)
3972        val = self.Value(self)
3973        val._type = self.Type(self, type_name)
3974        val.ldata = proxy_data
3975        return val
3976
3977    class StructBuilder():
3978        def __init__(self, dumper):
3979            self.dumper = dumper
3980            self.pattern = ''
3981            self.currentBitsize = 0
3982            self.fields = []
3983            self.autoPadNext = False
3984            self.maxAlign = 1
3985
3986        def addField(self, fieldSize, fieldCode=None, fieldIsStruct=False,
3987                     fieldName=None, fieldType=None, fieldAlign=1):
3988
3989            if fieldType is not None:
3990                fieldType = self.dumper.createType(fieldType)
3991            if fieldSize is None and fieldType is not None:
3992                fieldSize = fieldType.size()
3993            if fieldCode is None:
3994                fieldCode = '%ss' % fieldSize
3995
3996            if self.autoPadNext:
3997                self.currentBitsize = 8 * ((self.currentBitsize + 7) >> 3)  # Fill up byte.
3998                padding = (fieldAlign - (self.currentBitsize >> 3)) % fieldAlign
3999                #DumperBase.warn('AUTO PADDING AT %s BITS BY %s BYTES' % (self.currentBitsize, padding))
4000                field = self.dumper.Field(self.dumper, bitpos=self.currentBitsize,
4001                                          bitsize=padding * 8)
4002                self.pattern += '%ds' % padding
4003                self.currentBitsize += padding * 8
4004                self.fields.append(field)
4005                self.autoPadNext = False
4006
4007            if fieldAlign > self.maxAlign:
4008                self.maxAlign = fieldAlign
4009            #DumperBase.warn("MAX ALIGN: %s" % self.maxAlign)
4010
4011            field = self.dumper.Field(dumper=self.dumper, name=fieldName, type=fieldType,
4012                                      isStruct=fieldIsStruct, bitpos=self.currentBitsize,
4013                                      bitsize=fieldSize * 8)
4014
4015            self.pattern += fieldCode
4016            self.currentBitsize += fieldSize * 8
4017            self.fields.append(field)
4018
4019    def describeStruct(self, pattern):
4020        if pattern in self.structPatternCache:
4021            return self.structPatternCache[pattern]
4022        ptrSize = self.ptrSize()
4023        builder = self.StructBuilder(self)
4024        n = None
4025        typeName = ''
4026        readingTypeName = False
4027        for c in pattern:
4028            if readingTypeName:
4029                if c == '}':
4030                    readingTypeName = False
4031                    fieldType = self.createType(typeName)
4032                    fieldAlign = fieldType.alignment()
4033                    builder.addField(n, fieldIsStruct=True,
4034                                     fieldType=fieldType, fieldAlign=fieldAlign)
4035                    typeName = None
4036                    n = None
4037                else:
4038                    typeName += c
4039            elif c == 't':  # size_t
4040                builder.addField(ptrSize, self.ptrCode(), fieldAlign=ptrSize)
4041            elif c == 'p':  # Pointer as int
4042                builder.addField(ptrSize, self.ptrCode(), fieldAlign=ptrSize)
4043            elif c == 'P':  # Pointer as Value
4044                builder.addField(ptrSize, '%ss' % ptrSize, fieldAlign=ptrSize)
4045            elif c in ('d'):
4046                builder.addField(8, c, fieldAlign=ptrSize)  # fieldType = 'double' ?
4047            elif c in ('q', 'Q'):
4048                builder.addField(8, c, fieldAlign=ptrSize)
4049            elif c in ('i', 'I', 'f'):
4050                builder.addField(4, c, fieldAlign=4)
4051            elif c in ('h', 'H'):
4052                builder.addField(2, c, fieldAlign=2)
4053            elif c in ('b', 'B', 'c'):
4054                builder.addField(1, c, fieldAlign=1)
4055            elif c >= '0' and c <= '9':
4056                if n is None:
4057                    n = ''
4058                n += c
4059            elif c == 's':
4060                builder.addField(int(n), fieldAlign=1)
4061                n = None
4062            elif c == '{':
4063                readingTypeName = True
4064                typeName = ''
4065            elif c == '@':
4066                if n is None:
4067                    # Automatic padding depending on next item
4068                    builder.autoPadNext = True
4069                else:
4070                    # Explicit padding.
4071                    builder.currentBitsize = 8 * ((builder.currentBitsize + 7) >> 3)
4072                    padding = (int(n) - (builder.currentBitsize >> 3)) % int(n)
4073                    field = self.Field(self)
4074                    builder.pattern += '%ds' % padding
4075                    builder.currentBitsize += padding * 8
4076                    builder.fields.append(field)
4077                    n = None
4078            else:
4079                raise RuntimeError('UNKNOWN STRUCT CODE: %s' % c)
4080        pp = builder.pattern
4081        size = (builder.currentBitsize + 7) >> 3
4082        fields = builder.fields
4083        tailPad = (builder.maxAlign - size) % builder.maxAlign
4084        size += tailPad
4085        self.structPatternCache[pattern] = (pp, size, fields)
4086        return (pp, size, fields)
4087