1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
|
# Copyright 2018 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Helper library for defining XMethod implementations on C++ classes.
Include this library and then define python implementations of C++ methods
using the Class and member_function decorator functions.
"""
import gdb
import gdb.xmethod
import operator
import re
class MemberFunction(object):
def __init__(self, return_type, name, arguments, wrapped_function):
self.return_type = return_type
self.name = name
self.arguments = arguments
self.function_ = wrapped_function
def __call__(self, *args):
self.function_(*args)
def member_function(return_type, name, arguments):
"""Decorate a member function.
See Class decorator for example usage within a class.
Args:
return_type: The return type of the function (e.g. 'int')
name: The function name (e.g. 'sum')
arguments: The argument types for this function (e.g. ['int', 'int'])
Each type can be a string (e.g. 'int', 'std::string', 'T*') or a
function which constructs the return type. See CreateTypeResolver
for details about type resolution.
"""
def DefineMember(fn):
return MemberFunction(return_type, name, arguments, fn)
return DefineMember
def Class(class_name, template_types):
"""Decorate a python class with its corresponding C++ type.
Args:
class_name: The canonical string identifier for the class (e.g. base::Foo)
template_types: An array of names for each templated type (e.g. ['K',
'V'])
Example:
As an example, the following is an implementation of size() and operator[]
on std::__1::vector, functions which are normally inlined and not
normally callable from gdb.
@class_methods.Class('std::__1::vector', template_types=['T'])
class LibcppVector(object):
@class_methods.member_function('T&', 'operator[]', ['int'])
def element(obj, i):
return obj['__begin_'][i]
@class_methods.member_function('size_t', 'size', [])
def size(obj):
return obj['__end_'] - obj['__begin_']
Note:
Note that functions are looked up by the function name, which means that
functions cannot currently have overloaded implementations for different
arguments.
"""
class MethodWorkerWrapper(gdb.xmethod.XMethod):
"""Wrapper of an XMethodWorker class as an XMethod."""
def __init__(self, name, worker_class):
super(MethodWorkerWrapper, self).__init__(name)
self.name = name
self.worker_class = worker_class
class ClassMatcher(gdb.xmethod.XMethodMatcher):
"""Matches member functions of one class template."""
def __init__(self, obj):
super(ClassMatcher, self).__init__(class_name)
# Constructs a regular expression to match this type.
self._class_regex = re.compile(
'^' + re.escape(class_name) +
('<.*>' if len(template_types) > 0 else '') + '$')
# Construct a dictionary and array of methods
self.dict = {}
self.methods = []
for name in dir(obj):
attr = getattr(obj, name)
if not isinstance(attr, MemberFunction):
continue
name = attr.name
return_type = CreateTypeResolver(attr.return_type)
arguments = [CreateTypeResolver(arg) for arg in
attr.arguments]
method = MethodWorkerWrapper(
attr.name,
CreateTemplatedMethodWorker(return_type,
arguments, attr.function_))
self.methods.append(method)
def match(self, class_type, method_name):
if not re.match(self._class_regex, class_type.tag):
return None
templates = [class_type.template_argument(i) for i in
range(len(template_types))]
return [method.worker_class(templates) for method in self.methods
if method.name == method_name and method.enabled]
def CreateTypeResolver(type_desc):
"""Creates a callback which resolves to the appropriate type when
invoked.
This is a helper to allow specifying simple types as strings when
writing function descriptions. For complex cases, a callback can be
passed which will be invoked when template instantiation is known.
Args:
type_desc: A callback generating the type or a string description of
the type to lookup. Supported types are classes in the
template_classes array (e.g. T) which will be looked up when those
templated classes are known, or globally visible type names (e.g.
int, base::Foo).
Types can be modified by appending a '*' or '&' to denote a
pointer or reference.
If a callback is used, the callback will be passed an array of the
instantiated template types.
Note:
This does not parse complex types such as std::vector<T>::iterator,
to refer to types like these you must currently write a callback
which constructs the appropriate type.
"""
if callable(type_desc):
return type_desc
if type_desc == 'void':
return lambda T: None
if type_desc[-1] == '&':
inner_resolver = CreateTypeResolver(type_desc[:-1])
return lambda template_types: inner_resolver(template_types).reference()
if type_desc[-1] == '*':
inner_resolver = CreateTypeResolver(type_desc[:-1])
return lambda template_types: inner_resolver(template_types).pointer()
try:
template_index = template_types.index(type_desc)
return operator.itemgetter(template_index)
except ValueError:
return lambda template_types: gdb.lookup_type(type_desc)
def CreateTemplatedMethodWorker(return_callback, args_callbacks,
method_callback):
class TemplatedMethodWorker(gdb.xmethod.XMethodWorker):
def __init__(self, templates):
super(TemplatedMethodWorker, self).__init__()
self._templates = templates
def get_arg_types(self):
return [cb(self._templates) for cb in args_callbacks]
def get_result_type(self, obj):
return return_callback(self._templates)
def __call__(self, *args):
return method_callback(*args)
return TemplatedMethodWorker
def DefineClass(obj):
matcher = ClassMatcher(obj)
gdb.xmethod.register_xmethod_matcher(None, matcher)
return matcher
return DefineClass
|