1# Python side of the support for xmethods.
2# Copyright (C) 2013-2020 Free Software Foundation, Inc.
3
4# This program is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17"""Utilities for defining xmethods"""
18
19import gdb
20import re
21import sys
22
23
24if sys.version_info[0] > 2:
25    # Python 3 removed basestring and long
26    basestring = str
27    long = int
28
29
30class XMethod(object):
31    """Base class (or a template) for an xmethod description.
32
33    Currently, the description requires only the 'name' and 'enabled'
34    attributes.  Description objects are managed by 'XMethodMatcher'
35    objects (see below).  Note that this is only a template for the
36    interface of the XMethodMatcher.methods objects.  One could use
37    this class or choose to use an object which supports this exact same
38    interface.  Also, an XMethodMatcher can choose not use it 'methods'
39    attribute.  In such cases this class (or an equivalent) is not used.
40
41    Attributes:
42        name: The name of the xmethod.
43        enabled: A boolean indicating if the xmethod is enabled.
44    """
45
46    def __init__(self, name):
47        self.name = name
48        self.enabled = True
49
50
51class XMethodMatcher(object):
52    """Abstract base class for matching an xmethod.
53
54    When looking for xmethods, GDB invokes the `match' method of a
55    registered xmethod matcher to match the object type and method name.
56    The `match' method in concrete classes derived from this class should
57    return an `XMethodWorker' object, or a list of `XMethodWorker'
58    objects if there is a match (see below for 'XMethodWorker' class).
59
60    Attributes:
61        name: The name of the matcher.
62        enabled: A boolean indicating if the matcher is enabled.
63        methods: A sequence of objects of type 'XMethod', or objects
64            which have at least the attributes of an 'XMethod' object.
65            This list is used by the 'enable'/'disable'/'info' commands to
66            enable/disable/list the xmethods registered with GDB.  See
67            the 'match' method below to know how this sequence is used.
68            This attribute is None if the matcher chooses not have any
69            xmethods managed by it.
70    """
71
72    def __init__(self, name):
73        """
74        Args:
75            name: An identifying name for the xmethod or the group of
76                  xmethods returned by the `match' method.
77        """
78        self.name = name
79        self.enabled = True
80        self.methods = None
81
82    def match(self, class_type, method_name):
83        """Match class type and method name.
84
85        In derived classes, it should return an XMethodWorker object, or a
86        sequence of 'XMethodWorker' objects.  Only those xmethod workers
87        whose corresponding 'XMethod' descriptor object is enabled should be
88        returned.
89
90        Args:
91            class_type: The class type (gdb.Type object) to match.
92            method_name: The name (string) of the method to match.
93        """
94        raise NotImplementedError("XMethodMatcher match")
95
96
97class XMethodWorker(object):
98    """Base class for all xmethod workers defined in Python.
99
100    An xmethod worker is an object which matches the method arguments, and
101    invokes the method when GDB wants it to.  Internally, GDB first invokes the
102    'get_arg_types' method to perform overload resolution.  If GDB selects to
103    invoke this Python xmethod, then it invokes it via the overridden
104    '__call__' method.  The 'get_result_type' method is used to implement
105    'ptype' on the xmethod.
106
107    Derived classes should override the 'get_arg_types', 'get_result_type'
108    and '__call__' methods.
109    """
110
111    def get_arg_types(self):
112        """Return arguments types of an xmethod.
113
114        A sequence of gdb.Type objects corresponding to the arguments of the
115        xmethod are returned.  If the xmethod takes no arguments, then 'None'
116        or an empty sequence is returned.  If the xmethod takes only a single
117        argument, then a gdb.Type object or a sequence with a single gdb.Type
118        element is returned.
119        """
120        raise NotImplementedError("XMethodWorker get_arg_types")
121
122    def get_result_type(self, *args):
123        """Return the type of the result of the xmethod.
124
125        Args:
126            args: Arguments to the method.  Each element of the tuple is a
127                gdb.Value object.  The first element is the 'this' pointer
128                value.  These are the same arguments passed to '__call__'.
129
130        Returns:
131            A gdb.Type object representing the type of the result of the
132            xmethod.
133        """
134        raise NotImplementedError("XMethodWorker get_result_type")
135
136    def __call__(self, *args):
137        """Invoke the xmethod.
138
139        Args:
140            args: Arguments to the method.  Each element of the tuple is a
141                gdb.Value object.  The first element is the 'this' pointer
142                value.
143
144        Returns:
145            A gdb.Value corresponding to the value returned by the xmethod.
146            Returns 'None' if the method does not return anything.
147        """
148        raise NotImplementedError("XMethodWorker __call__")
149
150
151class SimpleXMethodMatcher(XMethodMatcher):
152    """A utility class to implement simple xmethod mathers and workers.
153
154    See the __init__ method below for information on how instances of this
155    class can be used.
156
157    For simple classes and methods, one can choose to use this class.  For
158    complex xmethods, which need to replace/implement template methods on
159    possibly template classes, one should implement their own xmethod
160    matchers and workers.  See py-xmethods.py in testsuite/gdb.python
161    directory of the GDB source tree for examples.
162    """
163
164    class SimpleXMethodWorker(XMethodWorker):
165        def __init__(self, method_function, arg_types):
166            self._arg_types = arg_types
167            self._method_function = method_function
168
169        def get_arg_types(self):
170            return self._arg_types
171
172        def __call__(self, *args):
173            return self._method_function(*args)
174
175
176    def __init__(self, name, class_matcher, method_matcher, method_function,
177                 *arg_types):
178        """
179        Args:
180            name: Name of the xmethod matcher.
181            class_matcher: A regular expression used to match the name of the
182                class whose method this xmethod is implementing/replacing.
183            method_matcher: A regular expression used to match the name of the
184                method this xmethod is implementing/replacing.
185            method_function: A Python callable which would be called via the
186                'invoke' method of the worker returned by the objects of this
187                class.  This callable should accept the object (*this) as the
188                first argument followed by the rest of the arguments to the
189                method. All arguments to this function should be gdb.Value
190                objects.
191            arg_types: The gdb.Type objects corresponding to the arguments that
192                this xmethod takes. It can be None, or an empty sequence,
193                or a single gdb.Type object, or a sequence of gdb.Type objects.
194        """
195        XMethodMatcher.__init__(self, name)
196        assert callable(method_function), (
197            "The 'method_function' argument to 'SimpleXMethodMatcher' "
198            "__init__ method should be a callable.")
199        self._method_function = method_function
200        self._class_matcher = class_matcher
201        self._method_matcher = method_matcher
202        self._arg_types = arg_types
203
204    def match(self, class_type, method_name):
205        cm = re.match(self._class_matcher, str(class_type.unqualified().tag))
206        mm = re.match(self._method_matcher, method_name)
207        if cm and mm:
208            return SimpleXMethodMatcher.SimpleXMethodWorker(
209                self._method_function, self._arg_types)
210
211
212# A helper function for register_xmethod_matcher which returns an error
213# object if MATCHER is not having the requisite attributes in the proper
214# format.
215
216def _validate_xmethod_matcher(matcher):
217    if not hasattr(matcher, "match"):
218        return TypeError("Xmethod matcher is missing method: match")
219    if not hasattr(matcher, "name"):
220        return TypeError("Xmethod matcher is missing attribute: name")
221    if not hasattr(matcher, "enabled"):
222        return TypeError("Xmethod matcher is missing attribute: enabled")
223    if not isinstance(matcher.name, basestring):
224        return TypeError("Attribute 'name' of xmethod matcher is not a "
225                         "string")
226    if matcher.name.find(";") >= 0:
227        return ValueError("Xmethod matcher name cannot contain ';' in it")
228
229
230# A helper function for register_xmethod_matcher which looks up an
231# xmethod matcher with NAME in LOCUS.  Returns the index of the xmethod
232# matcher in 'xmethods' sequence attribute of the LOCUS.  If NAME is not
233# found in LOCUS, then -1 is returned.
234
235def _lookup_xmethod_matcher(locus, name):
236    for i in range(0, len(locus.xmethods)):
237        if locus.xmethods[i].name == name:
238            return i
239    return -1
240
241
242def register_xmethod_matcher(locus, matcher, replace=False):
243    """Registers a xmethod matcher MATCHER with a LOCUS.
244
245    Arguments:
246        locus: The locus in which the xmethods should be registered.
247            It can be 'None' to indicate that the xmethods should be
248            registered globally. Or, it could be a gdb.Objfile or a
249            gdb.Progspace object in which the xmethods should be
250            registered.
251        matcher: The xmethod matcher to register with the LOCUS.  It
252            should be an instance of 'XMethodMatcher' class.
253        replace: If True, replace any existing xmethod matcher with the
254            same name in the locus.  Otherwise, if a matcher with the same name
255            exists in the locus, raise an exception.
256    """
257    err = _validate_xmethod_matcher(matcher)
258    if err:
259        raise err
260    if not locus:
261        locus = gdb
262    if locus == gdb:
263        locus_name = "global"
264    else:
265        locus_name = locus.filename
266    index = _lookup_xmethod_matcher(locus, matcher.name)
267    if index >= 0:
268        if replace:
269            del locus.xmethods[index]
270        else:
271            raise RuntimeError("Xmethod matcher already registered with "
272                               "%s: %s" % (locus_name, matcher.name))
273    if gdb.parameter("verbose"):
274        gdb.write("Registering xmethod matcher '%s' with %s' ...\n")
275    locus.xmethods.insert(0, matcher)
276