1# Type utilities.
2# Copyright (C) 2010-2024 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 working with gdb.Types."""
18
19import gdb
20
21
22def get_basic_type(type_):
23    """Return the "basic" type of a type.
24
25    Arguments:
26        type_: The type to reduce to its basic type.
27
28    Returns:
29        type_ with const/volatile is stripped away,
30        and typedefs/references converted to the underlying type.
31    """
32
33    while (
34        type_.code == gdb.TYPE_CODE_REF
35        or type_.code == gdb.TYPE_CODE_RVALUE_REF
36        or type_.code == gdb.TYPE_CODE_TYPEDEF
37    ):
38        if type_.code == gdb.TYPE_CODE_REF or type_.code == gdb.TYPE_CODE_RVALUE_REF:
39            type_ = type_.target()
40        else:
41            type_ = type_.strip_typedefs()
42    return type_.unqualified()
43
44
45def has_field(type_, field):
46    """Return True if a type has the specified field.
47
48    Arguments:
49        type_: The type to examine.
50            It must be one of gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION.
51        field: The name of the field to look up.
52
53    Returns:
54        True if the field is present either in type_ or any baseclass.
55
56    Raises:
57        TypeError: The type is not a struct or union.
58    """
59
60    type_ = get_basic_type(type_)
61    if type_.code != gdb.TYPE_CODE_STRUCT and type_.code != gdb.TYPE_CODE_UNION:
62        raise TypeError("not a struct or union")
63    for f in type_.fields():
64        if f.is_base_class:
65            if has_field(f.type, field):
66                return True
67        else:
68            # NOTE: f.name could be None
69            if f.name == field:
70                return True
71    return False
72
73
74def make_enum_dict(enum_type):
75    """Return a dictionary from a program's enum type.
76
77    Arguments:
78        enum_type: The enum to compute the dictionary for.
79
80    Returns:
81        The dictionary of the enum.
82
83    Raises:
84        TypeError: The type is not an enum.
85    """
86
87    if enum_type.code != gdb.TYPE_CODE_ENUM:
88        raise TypeError("not an enum type")
89    enum_dict = {}
90    for field in enum_type.fields():
91        # The enum's value is stored in "enumval".
92        enum_dict[field.name] = field.enumval
93    return enum_dict
94
95
96def deep_items(type_):
97    """Return an iterator that recursively traverses anonymous fields.
98
99    Arguments:
100        type_: The type to traverse.  It should be one of
101        gdb.TYPE_CODE_STRUCT or gdb.TYPE_CODE_UNION.
102
103    Returns:
104        an iterator similar to gdb.Type.iteritems(), i.e., it returns
105        pairs of key, value, but for any anonymous struct or union
106        field that field is traversed recursively, depth-first.
107    """
108    for k, v in type_.iteritems():
109        if k:
110            yield k, v
111        else:
112            for i in deep_items(v.type):
113                yield i
114
115
116class TypePrinter(object):
117    """The base class for type printers.
118
119    Instances of this type can be used to substitute type names during
120    'ptype'.
121
122    A type printer must have at least 'name' and 'enabled' attributes,
123    and supply an 'instantiate' method.
124
125    The 'instantiate' method must either return None, or return an
126    object which has a 'recognize' method.  This method must accept a
127    gdb.Type argument and either return None, meaning that the type
128    was not recognized, or a string naming the type.
129    """
130
131    def __init__(self, name):
132        self.name = name
133        self.enabled = True
134
135    def instantiate(self):
136        return None
137
138
139# Helper function for computing the list of type recognizers.
140def _get_some_type_recognizers(result, plist):
141    for printer in plist:
142        if printer.enabled:
143            inst = printer.instantiate()
144            if inst is not None:
145                result.append(inst)
146    return None
147
148
149def get_type_recognizers():
150    "Return a list of the enabled type recognizers for the current context."
151    result = []
152
153    # First try the objfiles.
154    for objfile in gdb.objfiles():
155        _get_some_type_recognizers(result, objfile.type_printers)
156    # Now try the program space.
157    _get_some_type_recognizers(result, gdb.current_progspace().type_printers)
158    # Finally, globals.
159    _get_some_type_recognizers(result, gdb.type_printers)
160
161    return result
162
163
164def apply_type_recognizers(recognizers, type_obj):
165    """Apply the given list of type recognizers to the type TYPE_OBJ.
166    If any recognizer in the list recognizes TYPE_OBJ, returns the name
167    given by the recognizer.  Otherwise, this returns None."""
168    for r in recognizers:
169        result = r.recognize(type_obj)
170        if result is not None:
171            return result
172    return None
173
174
175def register_type_printer(locus, printer):
176    """Register a type printer.
177    PRINTER is the type printer instance.
178    LOCUS is either an objfile, a program space, or None, indicating
179    global registration."""
180
181    if locus is None:
182        locus = gdb
183    locus.type_printers.insert(0, printer)
184