1# Copyright 2014-2017 Insight Software Consortium.
2# Copyright 2004-2009 Roman Yakovenko.
3# Distributed under the Boost Software License, Version 1.0.
4# See http://www.boost.org/LICENSE_1_0.txt
5
6"""
7defines classes, that describes "callable" declarations
8
9This modules contains definition for next C++ declarations:
10    - operator
11        - member
12        - free
13    - function
14        - member
15        - free
16    - constructor
17    - destructor
18"""
19from . import cpptypes
20from . import declaration
21from . import calldef_types
22
23
24# First level in hierarchy of calldef
25class argument_t(object):
26
27    """
28    class, that describes argument of "callable" declaration
29    """
30
31    def __init__(
32            self,
33            name='',
34            decl_type=None,
35            default_value=None,
36            attributes=None):
37        object.__init__(self)
38
39        self._name = name
40        self._default_value = default_value
41        self._decl_type = decl_type
42        self._attributes = attributes
43
44    def clone(self, **keywd):
45        """constructs new argument_t instance
46
47        return argument_t(
48            name=keywd.get('name', self.name),
49            decl_type=keywd.get('decl_type', self.decl_type),
50            default_value=keywd.get('default_value', self.default_value),
51            attributes=keywd.get('attributes', self.attributes ))
52
53        """
54        return argument_t(
55            name=keywd.get('name', self.name),
56            decl_type=keywd.get('decl_type', self.decl_type),
57            default_value=keywd.get('default_value', self.default_value),
58            attributes=keywd.get('attributes', self.attributes))
59
60    def __str__(self):
61        if self.ellipsis:
62            return "..."
63
64        if self.default_value is None:
65            return "%s %s" % (self.decl_type, self.name)
66
67        return "%s %s=%s" % (self.decl_type, self.name, self.default_value)
68
69    def __eq__(self, other):
70        if not isinstance(other, self.__class__):
71            return False
72        return self.name == other.name \
73            and self.default_value == other.default_value \
74            and self.decl_type == other.decl_type
75
76    def __hash__(self):
77        return (hash(self.__class__) ^
78                hash(self.name) ^
79                hash(self.default_value) ^
80                hash(self.decl_type))
81
82    def __ne__(self, other):
83        return not self.__eq__(other)
84
85    def __lt__(self, other):
86        if not isinstance(other, self.__class__):
87            return self.__class__.__name__ < other.__class__.__name__
88        return self.name < other.name \
89            and self.default_value < other.default_value \
90            and self.decl_type < other.decl_type
91
92    @property
93    def name(self):
94        """Argument name.
95            @type: str"""
96        return self._name
97
98    @name.setter
99    def name(self, name):
100        self._name = name
101
102    @property
103    def ellipsis(self):
104        """bool, if True argument represents ellipsis ( "..." )
105        in function definition"""
106        return isinstance(self.decl_type, cpptypes.ellipsis_t)
107
108    @property
109    def default_value(self):
110        """Argument's default value or None.
111            @type: str"""
112        return self._default_value
113
114    @default_value.setter
115    def default_value(self, default_value):
116        self._default_value = default_value
117
118    @property
119    def decl_type(self):
120        return self._decl_type
121
122    @decl_type.setter
123    def decl_type(self, decl_type):
124        self._decl_type = decl_type
125
126    @property
127    def attributes(self):
128        """GCCXML attributes, set using __attribute__((gccxml("...")))
129            @type: str"""
130        return self._attributes
131
132    @attributes.setter
133    def attributes(self, attributes):
134        self._attributes = attributes
135
136
137class calldef_t(declaration.declaration_t):
138
139    """base class for all "callable" declarations"""
140
141    def __init__(
142            self,
143            name='',
144            arguments=None,
145            exceptions=None,
146            return_type=None,
147            has_extern=False,
148            does_throw=True,
149            mangled=None):
150        declaration.declaration_t.__init__(self, name)
151        if not arguments:
152            arguments = []
153        self._arguments = arguments
154        if not exceptions:
155            exceptions = []
156        self._does_throw = does_throw
157        self._exceptions = exceptions
158        self._return_type = return_type
159        self._has_extern = has_extern
160        self._calling_convention = None
161        self._has_inline = None
162        self._mangled = mangled
163
164    def _get__cmp__call_items(self):
165        """
166        Implementation detail.
167
168        """
169
170        raise NotImplementedError()
171
172    def _get__cmp__items(self):
173        """
174        Implementation detail.
175
176        """
177
178        items = [
179            self.arguments,
180            self.return_type,
181            self.has_extern,
182            self.does_throw,
183            self.exceptions.sort(),
184            self.has_inline]
185
186        items.extend(self._get__cmp__call_items())
187        return items
188
189    def __eq__(self, other):
190        if not declaration.declaration_t.__eq__(self, other):
191            return False
192
193        return self.return_type == other.return_type \
194            and self.arguments == other.arguments \
195            and self.has_extern == other.has_extern \
196            and self.does_throw == other.does_throw \
197            and self.exceptions.sort() == other.exceptions.sort()
198
199    def __hash__(self):
200        return (super(calldef_t, self).__hash__() ^
201                hash(self.return_type) ^
202                hash(self.name))
203
204    @property
205    def arguments(self):
206        """The argument list.
207            @type: list of :class:`argument_t`"""
208        return self._arguments
209
210    @arguments.setter
211    def arguments(self, arguments):
212        self._arguments = arguments
213
214    @property
215    def has_ellipsis(self):
216        return self.arguments and self.arguments[-1].ellipsis
217
218    @property
219    def argument_types(self):
220        """list of all argument types"""
221        return [arg.decl_type for arg in self.arguments]
222
223    @property
224    def required_args(self):
225        """list of all required arguments"""
226        r_args = []
227        for arg in self.arguments:
228            if not arg.default_value:
229                r_args.append(arg)
230            else:
231                break
232        return r_args
233
234    @property
235    def optional_args(self):
236        """list of all optional arguments, the arguments that have default
237        value"""
238        return self.arguments[len(self.required_args):]
239
240    @property
241    def does_throw(self):
242        """If False, than function does not throw any exception.
243            In this case, function was declared with empty throw
244            statement."""
245        return self._does_throw
246
247    @does_throw.setter
248    def does_throw(self, does_throw):
249        self._does_throw = does_throw
250
251    @property
252    def exceptions(self):
253        """The list of exceptions.
254            @type: list of :class:`declaration_t`"""
255        return self._exceptions
256
257    @exceptions.setter
258    def exceptions(self, exceptions):
259        self._exceptions = exceptions
260
261    @property
262    def return_type(self):
263        """The type of the return value of the "callable" or None
264            (constructors).
265            @type: :class:`type_t`"""
266        return self._return_type
267
268    @return_type.setter
269    def return_type(self, return_type):
270        self._return_type = return_type
271
272    @property
273    def overloads(self):
274        """A list of overloaded "callables" (i.e. other callables with the
275        same name within the same scope.
276
277        @type: list of :class:`calldef_t`
278        """
279        if not self.parent:
280            return []
281        # finding all functions with the same name
282        return self.parent.calldefs(
283            name=self.name,
284            function=lambda decl: decl is not self,
285            allow_empty=True,
286            recursive=False)
287
288    @property
289    def has_extern(self):
290        """Was this callable declared as "extern"?
291            @type: bool"""
292        return self._has_extern
293
294    @has_extern.setter
295    def has_extern(self, has_extern):
296        self._has_extern = has_extern
297
298    @property
299    def has_inline(self):
300        """Was this callable declared with "inline" specifier
301            @type: bool"""
302        return self._has_inline
303
304    @has_inline.setter
305    def has_inline(self, has_inline):
306        self._has_inline = has_inline
307
308    def _report(self, *args, **keywd):
309        # Implementation detail. Will be removed when the deprecated
310        # i_depend_on_them method is dropped
311        from . import dependencies  # pylint: disable=R0401
312        return dependencies.dependency_info_t(self, *args, **keywd)
313
314    def i_depend_on_them(self, recursive=True):
315        self._warn_deprecated()
316        answer = []
317        if self.return_type:
318            answer.append(
319                self._report(self.return_type, hint="return type"))
320        for arg in self.arguments:
321            answer.append(self._report(arg.decl_type))
322        for exc in self.exceptions:
323            answer.append(self._report(exc, hint="exception"))
324        return answer
325
326    # pylint: disable=R0201
327    def guess_calling_convention(self):
328        """This function should be overriden in the derived classes and return
329        more-or-less successfull guess about calling convention"""
330        return calldef_types.CALLING_CONVENTION_TYPES.UNKNOWN
331
332    @property
333    def calling_convention(self):
334        """function calling convention. See
335            :class:CALLING_CONVENTION_TYPES class for possible values"""
336        if self._calling_convention is None:
337            self._calling_convention = \
338                calldef_types.CALLING_CONVENTION_TYPES.extract(self.attributes)
339            if not self._calling_convention:
340                self._calling_convention = self.guess_calling_convention()
341        return self._calling_convention
342
343    @calling_convention.setter
344    def calling_convention(self, cc):
345        self._calling_convention = cc
346
347    @property
348    def mangled(self):
349        """
350        Unique declaration name generated by the compiler.
351
352        :return: the mangled name
353        :rtype: str
354
355        """
356
357        return self.get_mangled_name()
358
359    @mangled.setter
360    def mangled(self, mangled):
361        self._mangled = mangled
362