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