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
26from dumper import Children, SubItem
27from utils import TypeCode, DisplayFormat
28import re
29
30#######################################################################
31#
32# SSE
33#
34#######################################################################
35
36
37def qdump____m128(d, value):
38    d.putEmptyValue()
39    d.putExpandable()
40    if d.isExpanded():
41        d.putArrayData(value.address(), 4, d.lookupType('float'))
42
43
44def qdump____m256(d, value):
45    d.putEmptyValue()
46    d.putExpandable()
47    if d.isExpanded():
48        d.putArrayData(value.address(), 8, d.lookupType('float'))
49
50
51def qdump____m512(d, value):
52    d.putEmptyValue()
53    d.putExpandable()
54    if d.isExpanded():
55        d.putArrayData(value.address(), 16, d.lookupType('float'))
56
57
58def qdump____m128d(d, value):
59    d.putEmptyValue()
60    d.putExpandable()
61    if d.isExpanded():
62        d.putArrayData(value.address(), 2, d.lookupType('double'))
63
64
65def qdump____m256d(d, value):
66    d.putEmptyValue()
67    d.putExpandable()
68    if d.isExpanded():
69        d.putArrayData(value.address(), 4, d.lookupType('double'))
70
71
72def qdump____m512d(d, value):
73    d.putEmptyValue()
74    d.putExpandable()
75    if d.isExpanded():
76        d.putArrayData(value.address(), 8, d.lookupType('double'))
77
78
79def qdump____m128i(d, value):
80    data = d.hexencode(value.data(16))
81    d.putValue(':'.join('%04x' % int(data[i:i + 4], 16) for i in range(0, 32, 4)))
82    d.putExpandable()
83    if d.isExpanded():
84        with Children(d):
85            addr = value.address()
86            d.putArrayItem('uint8x16', addr, 16, 'unsigned char')
87            d.putArrayItem('uint16x8', addr, 8, 'unsigned short')
88            d.putArrayItem('uint32x4', addr, 4, 'unsigned int')
89            d.putArrayItem('uint64x2', addr, 2, 'unsigned long long')
90
91
92def qdump____m256i(d, value):
93    data = d.hexencode(value.data(32))
94    d.putValue(':'.join('%04x' % int(data[i:i + 4], 16) for i in range(0, 64, 4)))
95    d.putExpandable()
96    if d.isExpanded():
97        with Children(d):
98            addr = value.address()
99            d.putArrayItem('uint8x32', addr, 32, 'unsigned char')
100            d.putArrayItem('uint16x16', addr, 16, 'unsigned short')
101            d.putArrayItem('uint32x8', addr, 8, 'unsigned int')
102            d.putArrayItem('uint64x4', addr, 4, 'unsigned long long')
103
104
105def qdump____m512i(d, value):
106    data = d.hexencode(value.data(64))
107    d.putValue(':'.join('%04x' % int(data[i:i + 4], 16) for i in range(0, 64, 4))
108               + ', ' + ':'.join('%04x' % int(data[i:i + 4], 16) for i in range(64, 128, 4)))
109    d.putExpandable()
110    if d.isExpanded():
111        with Children(d):
112            d.putArrayItem('uint32x16', value.address(), 16, 'unsigned int')
113            d.putArrayItem('uint64x8', value.address(), 8, 'unsigned long long')
114
115#######################################################################
116#
117# GSL
118#
119#######################################################################
120
121
122def qform__std__array():
123    return [DisplayFormat.ArrayPlot]
124
125
126def qdump__gsl__span(d, value):
127    size, pointer = value.split('pp')
128    d.putItemCount(size)
129    if d.isExpanded():
130        d.putPlotData(pointer, size, value.type[0])
131
132
133def qdump__gsl__byte(d, value):
134    d.putValue(value.integer())
135
136#######################################################################
137#
138# Eigen
139#
140#######################################################################
141
142#def qform__Eigen__Matrix():
143#    return 'Transposed'
144
145
146def qdump__Eigen__Matrix(d, value):
147    innerType = value.type[0]
148    argRow = value.type[1]
149    argCol = value.type[2]
150    options = value.type[3]
151    rowMajor = (int(options) & 0x1)
152    # The magic dimension value is -1 in Eigen3, but 10000 in Eigen2.
153    # 10000 x 10000 matrices are rare, vectors of dim 10000 less so.
154    # So 'fix' only the matrix case:
155    if argCol == 10000 and argRow == 10000:
156        argCol = -1
157        argRow = -1
158    if argCol != -1 and argRow != -1:
159        nrows = argRow
160        ncols = argCol
161        p = value.address()
162    else:
163        storage = value['m_storage']
164        nrows = storage['m_rows'].integer() if argRow == -1 else argRow
165        ncols = storage['m_cols'].integer() if argCol == -1 else argCol
166        p = storage['m_data'].pointer()
167    innerSize = innerType.size()
168    d.putValue('(%s x %s), %s' % (nrows, ncols, ['ColumnMajor', 'RowMajor'][rowMajor]))
169    d.putField('keeporder', '1')
170    d.putNumChild(nrows * ncols)
171
172    limit = 10000
173    nncols = min(ncols, limit)
174    nnrows = min(nrows, limit * limit / nncols)
175    if d.isExpanded():
176        #format = d.currentItemFormat() # format == 1 is 'Transposed'
177        with Children(d, nrows * ncols, childType=innerType):
178            if ncols == 1 or nrows == 1:
179                for i in range(0, min(nrows * ncols, 10000)):
180                    d.putSubItem(i, d.createValue(p + i * innerSize, innerType))
181            elif rowMajor == 1:
182                s = 0
183                for i in range(0, nnrows):
184                    for j in range(0, nncols):
185                        v = d.createValue(p + (i * ncols + j) * innerSize, innerType)
186                        d.putNamedSubItem(s, v, '[%d,%d]' % (i, j))
187                        s = s + 1
188            else:
189                s = 0
190                for j in range(0, nncols):
191                    for i in range(0, nnrows):
192                        v = d.createValue(p + (i + j * nrows) * innerSize, innerType)
193                        d.putNamedSubItem(s, v, '[%d,%d]' % (i, j))
194                        s = s + 1
195
196
197#######################################################################
198#
199# Nim
200#
201#######################################################################
202
203def qdump__NimStringDesc(d, value):
204    size, reserved = value.split('pp')
205    data = value.address() + 2 * d.ptrSize()
206    d.putBetterType('string')
207    d.putCharArrayHelper(data, size, d.createType('char'), 'utf8')
208
209
210def qdump__NimGenericSequence__(d, value, regex=r'^TY[\d]+$'):
211    code = value.type.stripTypedefs().code
212    if code == TypeCode.Struct:
213        size, reserved = d.split('pp', value)
214        data = value.address() + 2 * d.ptrSize()
215        typeobj = value['data'].type.dereference()
216        d.putItemCount(size)
217        d.putArrayData(data, size, typeobj)
218        d.putBetterType('%s (%s[%s])' % (value.type.name, typeobj.name, size))
219    else:
220        d.putEmptyValue()
221        d.putPlainChildren(value)
222
223
224def qdump__TNimNode(d, value):
225    name = value['name'].pointer()
226    d.putSimpleCharArray(name) if name != 0 else d.putEmptyValue()
227    if d.isExpanded():
228        with Children(d):
229            sons = value['sons'].pointer()
230            size = value['len'].integer()
231            for i in range(size):
232                val = d.createValue(d.extractPointer(sons + i * d.ptrSize()), value.type)
233                with SubItem(d, '[%d]' % i):
234                    d.putItem(val)
235            with SubItem(d, '[raw]'):
236                d.putPlainChildren(value)
237
238
239#######################################################################
240#
241# D
242#
243#######################################################################
244
245def cleanDType(type):
246    return str(type).replace('uns long long', 'string')
247
248
249def qdump_Array(d, value):
250    n = value['length']
251    p = value['ptr']
252    t = cleanDType(value.type)[7:]
253    d.putType('%s[%d]' % (t, n))
254    if t == 'char':
255        d.putValue(encodeCharArray(p, 100), 'local8bit')
256    else:
257        d.putEmptyValue()
258        d.putNumChild(n)
259        innerType = p.type
260        if d.isExpanded():
261            with Children(d, n, childType=innerType):
262                for i in range(0, n):
263                    d.putSubItem(i, p.dereference())
264                    p = p + 1
265
266
267def qdump_AArray(d, value):
268    #n = value['length']
269    # This ends up as _AArray_<key>_<value> with a single .ptr
270    # member of type void *. Not much that can be done here.
271    p = value['ptr']
272    t = cleanDType(value.type)[8:]
273    d.putType('%s]' % t.replace('_', '['))
274    d.putEmptyValue()
275    if d.isExpanded():
276        with Children(d, 1):
277            d.putSubItem('ptr', p)
278
279
280#######################################################################
281#
282# MPI
283#
284#######################################################################
285
286if False:
287
288    def qdump__tree_entry(d, value):
289        d.putValue('len: %s, offset: %s, type: %s' %
290                   (value['blocklength'], value['offset'], value['type']))
291
292    def qdump__tree(d, value):
293        count = value['count']
294        entries = value['entries']
295        base = value['base'].pointer()
296        d.putItemCount(count)
297        if d.isExpanded():
298            with Children(d):
299                with SubItem(d, 'tree'):
300                    d.putEmptyValue()
301                    d.putNoType()
302                    if d.isExpanded():
303                        with Children(d):
304                            for i in range(count):
305                                d.putSubItem(Item(entries[i], iname))
306                with SubItem(d, 'data'):
307                    d.putEmptyValue()
308                    d.putNoType()
309                    if d.isExpanded():
310                        with Children(d):
311                            for i in range(count):
312                                with SubItem(d, i):
313                                    entry = entries[i]
314                                    mpitype = str(entry['type'])
315                                    d.putType(mpitype)
316                                    length = int(entry['blocklength'])
317                                    offset = int(entry['offset'])
318                                    d.putValue('%s items at %s' % (length, offset))
319                                    if mpitype == 'MPI_INT':
320                                        innerType = 'int'
321                                    elif mpitype == 'MPI_CHAR':
322                                        innerType = 'char'
323                                    elif mpitype == 'MPI_DOUBLE':
324                                        innerType = 'double'
325                                    else:
326                                        length = 0
327                                    d.putNumChild(length)
328                                    if d.isExpanded():
329                                        with Children(d):
330                                            t = d.lookupType(innerType).pointer()
331                                            p = (base + offset).cast(t)
332                                            for j in range(length):
333                                                d.putSubItem(j, p.dereference())
334
335
336#######################################################################
337#
338# KDSoap
339#
340#######################################################################
341
342def qdump__KDSoapValue1(d, value):
343    inner = value['d']['d'].dereference()
344    d.putStringValue(inner['m_name'])
345    d.putPlainChildren(inner)
346
347
348def qdump__KDSoapValue(d, value):
349    p = (value.cast(d.lookupType('char*')) + 4).dereference().cast(d.lookupType('QString'))
350    d.putStringValue(p)
351    d.putPlainChildren(value['d']['d'].dereference())
352
353
354#######################################################################
355#
356# Webkit
357#
358#######################################################################
359
360def qdump__WTF__String(d, value):
361    # WTF::String -> WTF::RefPtr<WTF::StringImpl> -> WTF::StringImpl*
362    data = value['m_impl']['m_ptr']
363    d.checkPointer(data)
364
365    stringLength = int(data['m_length'])
366    d.check(0 <= stringLength and stringLength <= 100000000)
367
368    # WTF::StringImpl* -> WTF::StringImpl -> sizeof(WTF::StringImpl)
369    offsetToData = data.type.target().size()
370    bufferPtr = data.pointer() + offsetToData
371
372    is8Bit = data['m_is8Bit']
373    charSize = 1
374    if not is8Bit:
375        charSize = 2
376
377    d.putCharArrayHelper(bufferPtr, stringLength, charSize)
378
379
380#######################################################################
381#
382# Python
383#
384#######################################################################
385
386def get_python_interpreter_major_version(d):
387    key = 'python_interpreter_major_version'
388    if key in d.generalCache:
389        return d.generalCache[key]
390
391    e = "(char*)Py_GetVersion()"
392    result = d.nativeParseAndEvaluate(e)
393    result_str = str(result)
394    matches = re.search(r'(\d+?)\.(\d+?)\.(\d+?)', result_str)
395    if matches:
396        result_str = matches.group(1)
397    d.generalCache[key] = result_str
398    return result_str
399
400
401def is_python_3(d):
402    return get_python_interpreter_major_version(d) == '3'
403
404
405def repr_cache_decorator(namespace):
406    def real_decorator(func_to_decorate):
407        def wrapper(d, address):
408            if namespace in d.perStepCache and address in d.perStepCache[namespace]:
409                return d.perStepCache[namespace][address]
410
411            if namespace not in d.perStepCache:
412                d.perStepCache[namespace] = {}
413
414            if address == 0:
415                result_str = d.perStepCache[namespace][address] = "<nullptr>"
416                return result_str
417
418            result = func_to_decorate(d, address)
419            d.perStepCache[namespace][address] = result
420            return result
421        return wrapper
422    return real_decorator
423
424
425@repr_cache_decorator('py_object')
426def get_py_object_repr_helper(d, address):
427    # The code below is a long way to evaluate:
428    # ((PyBytesObject *)PyUnicode_AsEncodedString(PyObject_Repr(
429    #        (PyObject*){}), \"utf-8\", \"backslashreplace\"))->ob_sval"
430    # But with proper object cleanup.
431
432    e_decref = "Py_DecRef((PyObject *){})"
433
434    e = "PyObject_Repr((PyObject*){})"
435    repr_object_value = d.parseAndEvaluate(e.format(address))
436    repr_object_address = d.fromPointerData(repr_object_value.ldata)[0]
437
438    if is_python_3(d):
439        # Try to get a UTF-8 encoded string from the repr object.
440        e = "PyUnicode_AsEncodedString((PyObject*){}, \"utf-8\", \"backslashreplace\")"
441        string_object_value = d.parseAndEvaluate(e.format(repr_object_address))
442        string_object_address = d.fromPointerData(string_object_value.ldata)[0]
443
444        e = "(char*)(((PyBytesObject *){})->ob_sval)"
445        result = d.nativeParseAndEvaluate(e.format(string_object_address))
446
447        # It's important to stringify the result before any other evaluations happen.
448        result_str = str(result)
449
450        # Clean up.
451        d.nativeParseAndEvaluate(e_decref.format(string_object_address))
452
453    else:
454        # Retrieve non-unicode string.
455        e = "(char*)(PyString_AsString((PyObject*){}))"
456        result = d.nativeParseAndEvaluate(e.format(repr_object_address))
457
458        # It's important to stringify the result before any other evaluations happen.
459        result_str = str(result)
460
461    # Do some string stripping.
462    # FIXME when using cdb engine.
463    matches = re.search(r'.+?"(.+)"$', result_str)
464    if matches:
465        result_str = matches.group(1)
466
467    # Clean up.
468    d.nativeParseAndEvaluate(e_decref.format(repr_object_address))
469
470    return result_str
471
472
473@repr_cache_decorator('py_object_type')
474def get_py_object_type(d, object_address):
475    e = "((PyObject *){})->ob_type"
476    type_value = d.parseAndEvaluate(e.format(object_address))
477    type_address = d.fromPointerData(type_value.ldata)[0]
478    type_repr = get_py_object_repr_helper(d, type_address)
479    return type_repr
480
481
482@repr_cache_decorator('py_object_meta_type')
483def get_py_object_meta_type(d, object_address):
484    # The python3 object layout has a few more indirections.
485    if is_python_3(d):
486        e = "((PyObject *){})->ob_type->ob_base->ob_base->ob_type"
487    else:
488        e = "((PyObject *){})->ob_type->ob_type"
489    type_value = d.parseAndEvaluate(e.format(object_address))
490    type_address = d.fromPointerData(type_value.ldata)[0]
491    type_repr = get_py_object_repr_helper(d, type_address)
492    return type_repr
493
494
495@repr_cache_decorator('py_object_base_class')
496def get_py_object_base_class(d, object_address):
497    e = "((PyObject *){})->ob_type->tp_base"
498    base_value = d.parseAndEvaluate(e.format(object_address))
499    base_address = d.fromPointerData(base_value.ldata)[0]
500    base_repr = get_py_object_repr_helper(d, base_address)
501    return base_repr
502
503
504def get_py_object_repr(d, value):
505    address = value.address()
506    repr_available = False
507    try:
508        result = get_py_object_repr_helper(d, address)
509        d.putValue(d.hexencode(result), encoding='utf8')
510        repr_available = True
511    except:
512        d.putEmptyValue()
513
514    def sub_item(name, functor, address):
515        with SubItem(d, '[{}]'.format(name)):
516            sub_value = functor(d, address)
517            d.putValue(d.hexencode(sub_value), encoding='utf8')
518
519    d.putExpandable()
520    if d.isExpanded():
521        with Children(d):
522            if repr_available:
523                sub_item('class', get_py_object_type, address)
524                sub_item('super class', get_py_object_base_class, address)
525                sub_item('meta type', get_py_object_meta_type, address)
526            d.putFields(value)
527
528
529def qdump__PyTypeObject(d, value):
530    get_py_object_repr(d, value)
531
532
533def qdump___typeobject(d, value):
534    get_py_object_repr(d, value)
535
536
537def qdump__PyObject(d, value):
538    get_py_object_repr(d, value)
539
540
541def qdump__PyVarObject(d, value):
542    get_py_object_repr(d, value)
543
544
545#######################################################################
546#
547# Internal test
548#
549#######################################################################
550
551def qdump__QtcDumperTest_FieldAccessByIndex(d, value):
552    d.putValue(value["d"][2].integer())
553
554
555def qdump__QtcDumperTest_PointerArray(d, value):
556    foos = value["foos"]
557    d.putItemCount(10)
558    if d.isExpanded():
559        with Children(d, 10):
560            for i in d.childRange():
561                d.putSubItem(i, foos[i])
562
563
564def qdump__QtcDumperTest_BufArray(d, value):
565    maxItems = 1000
566    buffer = value['buffer']
567    count = int(value['count'])
568    objsize = int(value['objSize'])
569    valueType = value.type.templateArgument(0)
570    d.putItemCount(count, maxItems)
571    d.putNumChild(count)
572    if d.isExpanded():
573        with Children(d, count, maxNumChild=maxItems, childType=valueType):
574            for i in d.childRange():
575                d.putSubItem(i, (buffer + (i * objsize)).dereference().cast(valueType))
576
577
578def qdump__QtcDumperTest_List__NodeX(d, value):
579    typename = value.type.unqualified().name
580    pos0 = typename.find('<')
581    pos1 = typename.find('>')
582    tName = typename[pos0 + 1:pos1]
583    d.putBetterType('QtcDumperTest_List<' + tName + '>::Node')
584    d.putExpandable()
585    if d.isExpanded():
586        obj_type = d.lookupType(tName)
587        with Children(d):
588            d.putSubItem("this", value.cast(obj_type))
589            d.putFields(value)
590    #d.putPlainChildren(value)
591
592
593def qdump__QtcDumperTest_List(d, value):
594    innerType = value.type[0]
595    d.putExpandable()
596    p = value['root']
597    if d.isExpanded():
598        with Children(d):
599            d.putSubItem("[p]", p)
600            d.putSubItem("[root]", value["root"].cast(innerType))
601            d.putFields(value)
602    #d.putPlainChildren(value)
603
604
605def qdump__QtcDumperTest_String(d, value):
606    with Children(d):
607        first = d.hexdecode(d.putSubItem('first', value['first']).value)
608        second = d.hexdecode(d.putSubItem('second', value['second']).value)
609        third = d.hexdecode(d.putSubItem('third', value['third']).value)[:-1]
610    d.putValue(first + ', ' + second + ', ' + third)
611