File: class_methods.py

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (185 lines) | stat: -rw-r--r-- 6,449 bytes parent folder | download | duplicates (11)
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