]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - gdb/python/lib/gdb/dap/varref.py
95f00f5fe066fa0ab83bbaeff92a813346a68b6b
[thirdparty/binutils-gdb.git] / gdb / python / lib / gdb / dap / varref.py
1 # Copyright 2023 Free Software Foundation, Inc.
2
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 3 of the License, or
6 # (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
16 import gdb
17 from .startup import in_gdb_thread
18 from .server import client_bool_capability
19 from abc import abstractmethod
20 from contextlib import contextmanager
21
22
23 # A list of all the variable references created during this pause.
24 all_variables = []
25
26
27 # When the inferior is re-started, we erase all variable references.
28 # See the section "Lifetime of Objects References" in the spec.
29 @in_gdb_thread
30 def clear_vars(event):
31 global all_variables
32 all_variables = []
33
34
35 gdb.events.cont.connect(clear_vars)
36
37
38 # A null context manager. Python supplies one, starting in 3.7.
39 @contextmanager
40 def _null(**ignore):
41 yield
42
43
44 @in_gdb_thread
45 def apply_format(value_format):
46 """Temporarily apply the DAP ValueFormat.
47
48 This returns a new context manager that applies the given DAP
49 ValueFormat object globally, then restores gdb's state when finished."""
50 if value_format is not None and "hex" in value_format and value_format["hex"]:
51 return gdb.with_parameter("output-radix", 16)
52 return _null()
53
54
55 class BaseReference:
56 """Represent a variable or a scope.
57
58 This class is just a base class, some methods must be implemented in
59 subclasses.
60
61 The 'ref' field can be used as the variablesReference in the protocol.
62 """
63
64 @in_gdb_thread
65 def __init__(self, name):
66 """Create a new variable reference with the given name.
67
68 NAME is a string or None. None means this does not have a
69 name, e.g., the result of expression evaluation."""
70
71 global all_variables
72 all_variables.append(self)
73 self.ref = len(all_variables)
74 self.name = name
75 self.children = None
76
77 @in_gdb_thread
78 def to_object(self):
79 """Return a dictionary that describes this object for DAP.
80
81 The resulting object is a starting point that can be filled in
82 further. See the Scope or Variable types in the spec"""
83 result = {
84 "variablesReference": self.ref,
85 }
86 if self.name is not None:
87 result["name"] = str(self.name)
88 return result
89
90 def no_children(self):
91 """Call this to declare that this variable or scope has no children."""
92 self.ref = 0
93
94 @abstractmethod
95 def fetch_one_child(self, index):
96 """Fetch one child of this variable.
97
98 INDEX is the index of the child to fetch.
99 This should return a tuple of the form (NAME, VALUE), where
100 NAME is the name of the variable, and VALUE is a gdb.Value."""
101 return
102
103 @abstractmethod
104 def child_count(self):
105 """Return the number of children of this variable."""
106 return
107
108 @in_gdb_thread
109 def fetch_children(self, start, count):
110 """Fetch children of this variable.
111
112 START is the starting index.
113 COUNT is the number to return, with 0 meaning return all."""
114 if count == 0:
115 count = self.child_count()
116 if self.children is None:
117 self.children = [None] * self.child_count()
118 result = []
119 for idx in range(start, start + count):
120 if self.children[idx] is None:
121 (name, value) = self.fetch_one_child(idx)
122 self.children[idx] = VariableReference(name, value)
123 result.append(self.children[idx])
124 return result
125
126
127 class VariableReference(BaseReference):
128 """Concrete subclass of BaseReference that handles gdb.Value."""
129
130 def __init__(self, name, value, result_name="value"):
131 """Initializer.
132
133 NAME is the name of this reference, see superclass.
134 VALUE is a gdb.Value that holds the value.
135 RESULT_NAME can be used to change how the simple string result
136 is emitted in the result dictionary."""
137 super().__init__(name)
138 self.value = value
139 self.printer = gdb.printing.make_visualizer(value)
140 self.result_name = result_name
141 # We cache all the children we create.
142 self.child_cache = None
143 if not hasattr(self.printer, "children"):
144 self.no_children()
145 self.count = None
146 else:
147 self.count = -1
148
149 def cache_children(self):
150 if self.child_cache is None:
151 # This discards all laziness. This could be improved
152 # slightly by lazily evaluating children, but because this
153 # code also generally needs to know the number of
154 # children, it probably wouldn't help much. Note that
155 # this is only needed with legacy (non-ValuePrinter)
156 # printers.
157 self.child_cache = list(self.printer.children())
158 return self.child_cache
159
160 def child_count(self):
161 if self.count is None:
162 return None
163 if self.count == -1:
164 num_children = None
165 if isinstance(self.printer, gdb.ValuePrinter) and hasattr(
166 self.printer, "num_children"
167 ):
168 num_children = self.printer.num_children()
169 if num_children is None:
170 num_children = len(self.cache_children())
171 self.count = num_children
172 return self.count
173
174 def to_object(self):
175 result = super().to_object()
176 result[self.result_name] = str(self.printer.to_string())
177 num_children = self.child_count()
178 if num_children is not None:
179 if (
180 hasattr(self.printer, "display_hint")
181 and self.printer.display_hint() == "array"
182 ):
183 result["indexedVariables"] = num_children
184 else:
185 result["namedVariables"] = num_children
186 if client_bool_capability("supportsMemoryReferences"):
187 # https://github.com/microsoft/debug-adapter-protocol/issues/414
188 # changed DAP to allow memory references for any of the
189 # variable response requests, and to lift the restriction
190 # to pointer-to-function from Variable.
191 if self.value.type.strip_typedefs().code == gdb.TYPE_CODE_PTR:
192 result["memoryReference"] = hex(int(self.value))
193 if client_bool_capability("supportsVariableType"):
194 result["type"] = str(self.value.type)
195 return result
196
197 @in_gdb_thread
198 def fetch_one_child(self, idx):
199 if isinstance(self.printer, gdb.ValuePrinter) and hasattr(
200 self.printer, "child"
201 ):
202 return self.printer.child(idx)
203 else:
204 return self.cache_children()[idx]
205
206
207 @in_gdb_thread
208 def find_variable(ref):
209 """Given a variable reference, return the corresponding variable object."""
210 global all_variables
211 # Variable references are offset by 1.
212 ref = ref - 1
213 if ref < 0 or ref > len(all_variables):
214 raise Exception("invalid variablesReference")
215 return all_variables[ref]