1# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
2#
3# This file is part of the LibreOffice project.
4#
5# This Source Code Form is subject to the terms of the Mozilla Public
6# License, v. 2.0. If a copy of the MPL was not distributed with this
7# file, You can obtain one at http://mozilla.org/MPL/2.0/.
8#
9
10import gdb
11import re
12import six
13
14class UnsupportedType(Exception):
15    '''Represents exception thrown when an unsupported UNO type(like
16        array or union) is used.'''
17
18    def __init__(self, type):
19        self.type = type
20
21class UnknownType(Exception):
22    '''Represents exception thrown when an unknown UNO type is used.'''
23
24    def __init__(self, type):
25        self.type = type
26
27class TypeClass(object):
28    '''Represents type class of UNO type.'''
29
30    # type class of void
31    VOID = 0
32    # type class of char
33    CHAR = 1
34    # type class of boolean
35    BOOLEAN = 2
36    # type class of byte
37    BYTE = 3
38    # type class of short
39    SHORT = 4
40    # type class of unsigned short
41    UNSIGNED_SHORT = 5
42    # type class of long
43    LONG = 6
44    # type class of unsigned long
45    UNSIGNED_LONG = 7
46    # type class of hyper
47    HYPER = 8
48    # type class of unsigned hyper
49    UNSIGNED_HYPER = 9
50    # type class of float
51    FLOAT = 10
52    # type class of double
53    DOUBLE = 11
54    # type class of string
55    STRING = 12
56    # type class of type
57    TYPE = 13
58    # type class of any
59    ANY = 14
60    # type class of enum
61    ENUM = 15
62    # type class of typedef
63    TYPEDEF = 16
64    # type class of struct
65    STRUCT = 17
66
67    # type class of exception
68    EXCEPTION = 19
69    # type class of sequence
70    SEQUENCE = 20
71
72    # type class of interface
73    INTERFACE = 22
74    # type class of service (not implemented)
75    SERVICE = 23
76    # type class of module (not implemented)
77    MODULE = 24
78    # type class of interface method
79    INTERFACE_METHOD = 25
80    # type class of interface attribute
81    INTERFACE_ATTRIBUTE = 26
82    # type class of unknown type
83    UNKNOWN = 27
84    # type class of properties
85    PROPERTY = 28
86    # type class of constants
87    CONSTANT = 29
88    # type class of constants groups
89    CONSTANTS = 30
90    # type class of singletons
91    SINGLETON = 31
92
93class TemplateType(object):
94
95    def __init__(self, template, *args):
96        self.template = template
97        self.args = args
98
99    def __str__(self):
100        argtypes = [str(gdb.lookup_type(str(arg)).strip_typedefs()) for arg in self.args]
101        return self.template + '<' + ', '.join(argtypes) + '>'
102
103class Type(object):
104    '''Describes a UNO type.'''
105
106    def __init__(self, typeclass, tag):
107        '''Constructs a new Type.
108            @param[in] typeclass value of com::sun::star::uno::TypeClass
109            @param[in] tag UNO name of the type
110        '''
111        self.typeclass = typeclass
112        self.tag = tag
113        # C++ name of the type
114        self.typename = None
115
116    def type(self):
117        '''Gets gdb.Type for the type'''
118        if self.typename:
119            return gdb.lookup_type(str(self.typename))
120        return None
121
122    @staticmethod
123    def uno2cpp(typename):
124        return str(typename).replace('.', '::')[1:-1]
125
126    def strip_typedefs(self):
127        copy = self.copy()
128        copy.typename = self._strip_typedefs(self.typename)
129        return copy
130
131    def _strip_typedefs(self, typename):
132        template_args = re.compile('([^<]+)(<.*>)')
133        match = template_args.match(typename)
134        type = self._lookup_type(match.group(1))
135        types = []
136        if match.group(2):
137            list_delim = re.compile(', *')
138            # FIXME: this does not work with nested templates
139            for arg in match.group(2).split(list_delim):
140                types.append(self._lookup_type(arg))
141
142        typename = str(type)
143        if not types.empty():
144            typename += '<' + types.join(', ') + '>'
145
146        return typename
147
148    def _lookup_type(self, typename):
149        if typename != '':
150            type = gdb.lookup_type(typename)
151            if type:
152                type = type.strip_typedefs()
153        return type
154
155def make_uno_type(val):
156    '''Creates a UNO type from gdb.Value of type
157        com::sun::star::uno::Type, typelib_TypeDescription, or
158        typelib_TypeDescriptionReference
159    '''
160
161    cssu_type = 'com::sun::star::uno::Type'
162    type_desc = '_typelib_TypeDescription'
163    type_descs =(
164            type_desc,
165            '_typelib_CompoundTypeDescription',
166            '_typelib_StructTypeDescription',
167            '_typelib_IndirectTypeDescription',
168            '_typelib_EnumTypeDescription',
169            '_typelib_InterfaceMemberTypeDescription',
170            '_typelib_InterfaceMethodTypeDescription',
171            '_typelib_InterfaceAttributeTypeDescription',
172            '_typelib_InterfaceTypeDescription'
173    )
174    type_desc_ref = '_typelib_TypeDescriptionReference'
175
176    type = val.type.strip_typedefs()
177
178    if type.tag == cssu_type:
179        pvalue = val['_pType']
180        assert pvalue
181        val = pvalue.dereference()
182        type = val.type.strip_typedefs()
183
184    while type.tag == type_desc_ref:
185        pvalue = val['pType']
186        assert pvalue
187        val = pvalue.dereference()
188        type = val.type.strip_typedefs()
189
190    if type.tag not in type_descs:
191        return None
192
193    # determination of the UNO type
194    full_val = val
195    if type.tag != type_desc:
196        while 'aBase' in val:
197            val = val['aBase']
198    type_class = int(val['eTypeClass'])
199    name = val['pTypeName'].dereference()
200    uno_type = None
201    if type_class == TypeClass.VOID:
202        uno_type = VoidType()
203    elif type_class == TypeClass.CHAR:
204        uno_type = PrimitiveType(type_class, name, 'sal_Char')
205    elif type_class == TypeClass.BOOLEAN:
206        uno_type = PrimitiveType(type_class, name, 'sal_Bool')
207    elif type_class == TypeClass.BYTE:
208        uno_type = PrimitiveType(type_class, name, 'sal_Int8')
209    elif type_class == TypeClass.SHORT:
210        uno_type = PrimitiveType(type_class, name, 'sal_Int16')
211    elif type_class == TypeClass.UNSIGNED_SHORT:
212        uno_type = PrimitiveType(type_class, name, 'sal_uInt16')
213    elif type_class == TypeClass.LONG:
214        uno_type = PrimitiveType(type_class, name, 'sal_Int32')
215    elif type_class == TypeClass.UNSIGNED_LONG:
216        uno_type = PrimitiveType(type_class, name, 'sal_uInt32')
217    elif type_class == TypeClass.HYPER:
218        uno_type = PrimitiveType(type_class, name, 'sal_Int64')
219    elif type_class == TypeClass.UNSIGNED_HYPER:
220        uno_type = PrimitiveType(type_class, name, 'sal_uInt64')
221    elif type_class == TypeClass.FLOAT:
222        uno_type = PrimitiveType(type_class, name, 'float')
223    elif type_class == TypeClass.DOUBLE:
224        uno_type = PrimitiveType(type_class, name, 'double')
225    elif type_class == TypeClass.STRING:
226        uno_type = PrimitiveType(type_class, name, 'rtl::OUString')
227    elif type_class == TypeClass.TYPE:
228        uno_type = PrimitiveType(type_class, name, 'com::sun::star::uno::Type')
229    elif type_class == TypeClass.ANY:
230        uno_type = PrimitiveType(type_class, name, 'com::sun::star::uno::Any')
231    elif type_class == TypeClass.ENUM:
232        uno_type = EnumType(val, full_val)
233    elif type_class == TypeClass.TYPEDEF:
234        pass
235    elif type_class == TypeClass.STRUCT:
236        uno_type = StructType(val, full_val)
237    elif type_class == TypeClass.EXCEPTION:
238        uno_type = CompoundType(val, full_val)
239    elif type_class == TypeClass.SEQUENCE:
240        uno_type = IndirectType(val, full_val)
241    elif type_class == TypeClass.INTERFACE:
242        uno_type = InterfaceType(val, full_val)
243    elif type_class == TypeClass.SERVICE:
244        raise UnsupportedType('service')
245    elif type_class == TypeClass.MODULE:
246        raise UnsupportedType('module')
247    elif type_class == TypeClass.INTERFACE_METHOD:
248        uno_type = InterfaceMethodType(val, full_val)
249    elif type_class == TypeClass.INTERFACE_ATTRIBUTE:
250        uno_type = InterfaceAttributeType(val, full_val)
251    elif type_class == TypeClass.UNKNOWN:
252        raise UnknownType(type)
253    elif type_class == TypeClass.PROPERTY:
254        pass
255    elif type_class == TypeClass.CONSTANT:
256        pass
257    elif type_class == TypeClass.CONSTANTS:
258        pass
259    elif type_class == TypeClass.SINGLETON:
260        pass
261    else:
262        raise UnknownType(type)
263
264    assert uno_type
265    return uno_type
266
267def uno_cast(type, val):
268    '''Casts val or pointer to UNO type represented by type'''
269    if val.type.code == gdb.TYPE_CODE_PTR:
270        return val.cast(type.type().pointer())
271    else:
272        return val.cast(type.type())
273
274class VoidType(Type):
275
276    def __init__(self):
277        super(VoidType, self).__init__(TypeClass.VOID, "void")
278        self.typename = "void"
279
280class PrimitiveType(Type):
281
282    def __init__(self, typeclass, typename_uno, typename_cpp):
283        super(PrimitiveType, self).__init__(typeclass, typename_uno)
284        self.typename = str(typename_cpp)
285
286class CompoundType(Type):
287
288    def __init__(self, type, full_type):
289        super(CompoundType, self).__init__(type['eTypeClass'], type['pTypeName'].dereference())
290        self.typename = self.uno2cpp(self.tag)
291        self._type = full_type
292
293    class _iterator(six.Iterator):
294
295        def __init__(self, count, types, names):
296            self.count = count
297            self.members = members
298            self.names = names
299            self.pos = 0
300
301        def __iter__(self):
302            return self
303
304        def __next__(self):
305            assert self.pos >= 0 and self.pos <= self.count
306            if self.pos == self.count:
307                raise StopIteration
308
309            pmember = self.members[self.pos]
310            assert pmember
311            pname = self.names[self.i]
312            assert pname
313            self.pos = self.pos + 1
314            member = make_uno_type(pmember.dereference())
315            assert member
316            name = str(pname.dereference())
317            return (name, member)
318
319    def attributes(self):
320        return _iterator(self._type['nMembers'], self._type['ppTypeRefs'],
321                self._type['ppMemberNames'])
322
323class StructType(CompoundType):
324
325    def __init__(self, type, full_type):
326        full_type = full_type.cast(gdb.lookup_type('_typelib_StructTypeDescription'))
327        super(StructType, self).__init__(type, full_type['aBase'])
328
329class IndirectType(Type):
330
331    def __init__(self, type, full_type):
332        super(IndirectType, self).__init__(type['eTypeClass'], type['pTypeName'].dereference())
333        full_type = full_type.cast(gdb.lookup_type('_typelib_IndirectTypeDescription'))
334        pelem = full_type['pType']
335        assert pelem
336        self.element = make_uno_type(pelem.dereference())
337        assert self.element
338        self.typename = TemplateType('com::sun::star::uno::Sequence', self.element.typename)
339
340class EnumType(Type):
341
342    def __init__(self, type, full_type):
343        super(EnumType, self).__init__(TypeClass.ENUM, type['pTypeName'].dereference())
344        self.typename = self.uno2cpp(self.tag)
345        self._type = full_type.cast(gdb.lookup_type('_typelib_EnumTypeDescription'))
346
347    class _iterator(six.Iterator):
348
349        def __init__(self, count, values, names):
350            self.count = count
351            self.values = values
352            self.names = names
353            self.pos = 0
354
355        def __iter__(self):
356            return self
357
358        def __next__(self):
359            assert self.pos >= 0 and self.pos <= self.count
360            if self.pos == self.count:
361                raise StopIteration
362
363            pvalue = self.values[self.pos]
364            assert pvalue
365            pname = self.names[self.pos]
366            assert pname
367            self.pos = self.pos + 1
368            val = int(pvalue.dereference())
369            name = str(pname.dereference())
370            return (name, val)
371
372    def values(self):
373        return _iterator(self._type['nEnumValues'],
374                self._type['ppEnumNames'], self._type['pEnumValues'])
375
376    def default_value(self):
377        return self._type['nDefaultEnumValue']
378
379class InterfaceMemberType(Type):
380
381    def __init__(self, type, full_type):
382        super(InterfaceMemberType, self).__init__(type['eTypeClass'], type['pTypeName'].dereference())
383        (interface, delim, member) = self.tag.partition('::')
384        self.typename = self.uno2cpp(interface) + '::*' + member
385        full_type = full_type.cast(gdb.lookup_type('_typelib_InterfaceMemberTypeDescription'))
386        self.position = full_type['nPosition']
387        pname = full_type['pMemberName']
388        assert pname
389        self.name = pname.dereference()
390
391class InterfaceMethodType(InterfaceMemberType):
392
393    def __init__(self, type, full_type):
394        full_type = full_type.cast(gdb.lookup_type('_typelib_InterfaceMethodTypeDescription'))
395        super(InterfaceMethodType, self).__init__(type, full_type['aBase'])
396        pret = full_type['pReturnTypeRef']
397        assert pret
398        self.return_type = make_uno_type(pret.dereference())
399        assert self.return_type
400        self.oneway = full_type['bOneWay']
401        self._type = full_type
402
403    class _iterator(six.Iterator):
404
405        def __init__(self, count, values):
406            self.count = count
407            self.values = values
408            self.pos = 0
409            assert values
410
411        def __iter__(self):
412            return self
413
414        def __next__(self):
415            assert self.pos >= 0 and self.pos <= self.count
416            if self.pos == self.count:
417                raise StopIteration
418
419            val = self.values[self.pos]
420            self.pos = self.pos + 1
421            return val
422
423    class parameter(tuple):
424
425        def __init__(self, type):
426            self.__init_tuple(type)
427            self.input = type['bIn']
428            self.output = type['bOut']
429
430        def _init_tuple(self, type):
431            pname = self['pName']
432            assert pname
433            ptype = self['pTypeRef']
434            assert ptype
435            name = str(pname.dereference())
436            type = make_uno_type(ptype.dereference())
437            assert type
438            super(parameter, self).__init__(name, type)
439
440    def parameters(self):
441        for param in _iterator(self._type['nParams'], self._type['pParams']):
442            yield parameter(param)
443
444    def exceptions(self):
445        def make_exception(self, pex):
446            assert pex
447            ex = make_uno_type(pex.dereference())
448            assert ex
449            return ex
450
451        for ex in _iterator(
452                self._type['nExceptions'], self._type['ppExceptions']):
453            yield make_exception(ex)
454
455class InterfaceAttributeType(InterfaceMemberType):
456
457    def __init__(self, type, full_type):
458        full_type = full_type.cast(gdb.lookup_type('_typelib_InterfaceAttributeTypeDescription'))
459        super(InterfaceAttributeType, self).__init__(type, full_type['aBase'])
460        self.readonly = full_type['bReadOnly']
461        ptype = full_type['pAttributeTypeRef']
462        assert ptype
463        self.type = make_uno_type(ptype.dereference())
464        assert self.type
465
466class MembersNotInitialized(Exception):
467    '''Represents exception raised when interface type' members haven't
468        been initialized(i.e. just level 1 initialization has been
469        performed)'''
470    pass
471
472class InterfaceType(Type):
473
474    def __init__(self, type, full_type):
475        super(InterfaceType, self).__init__(TypeClass.INTERFACE, type['pTypeName'].dereference())
476        assert int(type['eTypeClass']) == TypeClass.INTERFACE
477        self.typename = self.uno2cpp(self.tag)
478        full_type = full_type.cast(gdb.lookup_type('_typelib_InterfaceTypeDescription'))
479        self.uik = full_type['aUik']
480        self._type = full_type
481
482    class _iterator(six.Iterator):
483
484        def __init__(self, count, values):
485            assert values
486            self.count = count
487            self.values = values
488            self.pos = 0
489
490        def __iter__(self):
491            return self
492
493        def __next__(self):
494            assert self.pos >= 0 and self.pos <= self.count
495            pvalue = self.values[self.pos]
496            assert pvalue
497            self.pos = self.pos + 1
498            uno = make_uno_type(pvalue.dereference())
499            assert uno
500            return uno
501
502    def members(self):
503        return __members(self._type['nMembers'], self._type['ppMembers'])
504
505    def all_members(self):
506        return __members(self._type['nAllMembers'], self._type['ppAllMembers'])
507
508    def __members(count, values):
509        if values == 0:
510            raise MembersNotInitialized
511        return _iterator(count, values)
512
513    def bases(self):
514        return _iterator(self._type['nBaseTypes'], self._type['ppBaseTypes'])
515
516# vim:set shiftwidth=4 softtabstop=4 expandtab:
517