]>
git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - gdb/python/lib/gdb/dap/varref.py
1 # Copyright 2023-2024 Free Software Foundation, Inc.
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.
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.
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/>.
17 from .startup
import in_gdb_thread
, DAPException
18 from .server
import client_bool_capability
19 from abc
import ABC
, abstractmethod
20 from collections
import defaultdict
21 from contextlib
import contextmanager
24 # A list of all the variable references created during this pause.
28 # When the inferior is re-started, we erase all variable references.
29 # See the section "Lifetime of Objects References" in the spec.
31 def clear_vars(event
):
36 gdb
.events
.cont
.connect(clear_vars
)
39 # A null context manager. Python supplies one, starting in 3.7.
46 def apply_format(value_format
):
47 """Temporarily apply the DAP ValueFormat.
49 This returns a new context manager that applies the given DAP
50 ValueFormat object globally, then restores gdb's state when finished."""
51 if value_format
is not None and "hex" in value_format
and value_format
["hex"]:
52 return gdb
.with_parameter("output-radix", 16)
56 class BaseReference(ABC
):
57 """Represent a variable or a scope.
59 This class is just a base class, some methods must be implemented in
62 The 'ref' field can be used as the variablesReference in the protocol.
66 def __init__(self
, name
):
67 """Create a new variable reference with the given name.
69 NAME is a string or None. None means this does not have a
70 name, e.g., the result of expression evaluation."""
73 all_variables
.append(self
)
74 self
.ref
= len(all_variables
)
80 """Return a dictionary that describes this object for DAP.
82 The resulting object is a starting point that can be filled in
83 further. See the Scope or Variable types in the spec"""
84 result
= {"variablesReference": self
.ref
if self
.has_children() else 0}
85 if self
.name
is not None:
86 result
["name"] = str(self
.name
)
90 def has_children(self
):
91 """Return True if this object has children."""
94 def reset_children(self
):
95 """Reset any cached information about the children of this object."""
96 # A list of all the children. Each child is a BaseReference
99 # Map from the name of a child to a BaseReference.
101 # Keep track of how many duplicates there are of a given name,
102 # so that unique names can be generated. Map from base name
104 self
.name_counts
= defaultdict(lambda: 1)
107 def fetch_one_child(self
, index
):
108 """Fetch one child of this variable.
110 INDEX is the index of the child to fetch.
111 This should return a tuple of the form (NAME, VALUE), where
112 NAME is the name of the variable, and VALUE is a gdb.Value."""
116 def child_count(self
):
117 """Return the number of children of this variable."""
120 # Helper method to compute the final name for a child whose base
121 # name is given. Updates the name_counts map. This is used to
122 # handle shadowing -- in DAP, the adapter is responsible for
123 # making sure that all the variables in a a given container have
125 # https://github.com/microsoft/debug-adapter-protocol/issues/141
127 # https://github.com/microsoft/debug-adapter-protocol/issues/149
128 def _compute_name(self
, name
):
129 if name
in self
.by_name
:
130 self
.name_counts
[name
] += 1
131 # In theory there's no safe way to compute a name, because
132 # a pretty-printer might already be generating names of
133 # that form. In practice I think we should not worry too
135 name
= name
+ " #" + str(self
.name_counts
[name
])
139 def fetch_children(self
, start
, count
):
140 """Fetch children of this variable.
142 START is the starting index.
143 COUNT is the number to return, with 0 meaning return all.
144 Returns an iterable of some kind."""
146 count
= self
.child_count()
147 if self
.children
is None:
148 self
.children
= [None] * self
.child_count()
149 for idx
in range(start
, start
+ count
):
150 if self
.children
[idx
] is None:
151 (name
, value
) = self
.fetch_one_child(idx
)
152 name
= self
._compute
_name
(name
)
153 var
= VariableReference(name
, value
)
154 self
.children
[idx
] = var
155 self
.by_name
[name
] = var
156 yield self
.children
[idx
]
159 def find_child_by_name(self
, name
):
160 """Find a child of this variable, given its name.
162 Returns the value of the child, or throws if not found."""
163 # A lookup by name can only be done using names previously
164 # provided to the client, so we can simply rely on the by-name
166 if name
in self
.by_name
:
167 return self
.by_name
[name
]
168 raise DAPException("no variable named '" + name
+ "'")
171 class VariableReference(BaseReference
):
172 """Concrete subclass of BaseReference that handles gdb.Value."""
174 def __init__(self
, name
, value
, result_name
="value"):
177 NAME is the name of this reference, see superclass.
178 VALUE is a gdb.Value that holds the value.
179 RESULT_NAME can be used to change how the simple string result
180 is emitted in the result dictionary."""
181 super().__init
__(name
)
182 self
.result_name
= result_name
186 # Internal method to update local data when the value changes.
187 def _update_value(self
):
188 self
.reset_children()
189 self
.printer
= gdb
.printing
.make_visualizer(self
.value
)
190 self
.child_cache
= None
191 if self
.has_children():
196 def assign(self
, value
):
197 """Assign VALUE to this object and update."""
198 self
.value
.assign(value
)
201 def has_children(self
):
202 return hasattr(self
.printer
, "children")
204 def cache_children(self
):
205 if self
.child_cache
is None:
206 # This discards all laziness. This could be improved
207 # slightly by lazily evaluating children, but because this
208 # code also generally needs to know the number of
209 # children, it probably wouldn't help much. Note that
210 # this is only needed with legacy (non-ValuePrinter)
212 self
.child_cache
= list(self
.printer
.children())
213 return self
.child_cache
215 def child_count(self
):
216 if self
.count
is None:
220 if isinstance(self
.printer
, gdb
.ValuePrinter
) and hasattr(
221 self
.printer
, "num_children"
223 num_children
= self
.printer
.num_children()
224 if num_children
is None:
225 num_children
= len(self
.cache_children())
226 self
.count
= num_children
230 result
= super().to_object()
231 result
[self
.result_name
] = str(self
.printer
.to_string())
232 num_children
= self
.child_count()
233 if num_children
is not None:
235 hasattr(self
.printer
, "display_hint")
236 and self
.printer
.display_hint() == "array"
238 result
["indexedVariables"] = num_children
240 result
["namedVariables"] = num_children
241 if client_bool_capability("supportsMemoryReferences"):
242 # https://github.com/microsoft/debug-adapter-protocol/issues/414
243 # changed DAP to allow memory references for any of the
244 # variable response requests, and to lift the restriction
245 # to pointer-to-function from Variable.
246 if self
.value
.type.strip_typedefs().code
== gdb
.TYPE_CODE_PTR
:
247 result
["memoryReference"] = hex(int(self
.value
))
248 if client_bool_capability("supportsVariableType"):
249 result
["type"] = str(self
.value
.type)
253 def fetch_one_child(self
, idx
):
254 if isinstance(self
.printer
, gdb
.ValuePrinter
) and hasattr(
255 self
.printer
, "child"
257 (name
, val
) = self
.printer
.child(idx
)
259 (name
, val
) = self
.cache_children()[idx
]
260 # A pretty-printer can return something other than a
261 # gdb.Value, but it must be convertible.
262 if not isinstance(val
, gdb
.Value
):
268 def find_variable(ref
):
269 """Given a variable reference, return the corresponding variable object."""
271 # Variable references are offset by 1.
273 if ref
< 0 or ref
> len(all_variables
):
274 raise DAPException("invalid variablesReference")
275 return all_variables
[ref
]