87f2ed7547f9569386df678623c372473694e0a0
[binutils-gdb.git] / gdb / python / lib / gdb / dap / scopes.py
1 # Copyright 2022-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
18 from .frames import frame_for_id
19 from .startup import send_gdb_with_response, in_gdb_thread
20 from .server import request
21 from .varref import BaseReference
22
23
24 # Map DAP frame IDs to scopes. This ensures that scopes are re-used.
25 frame_to_scope = {}
26
27
28 # When the inferior is re-started, we erase all scope references. See
29 # the section "Lifetime of Objects References" in the spec.
30 @in_gdb_thread
31 def clear_scopes(event):
32 global frame_to_scope
33 frame_to_scope = {}
34
35
36 gdb.events.cont.connect(clear_scopes)
37
38
39 class _ScopeReference(BaseReference):
40 def __init__(self, name, hint, frame, var_list):
41 super().__init__(name)
42 self.hint = hint
43 self.frame = frame
44 self.inf_frame = frame.inferior_frame()
45 self.func = frame.function()
46 self.line = frame.line()
47 # VAR_LIST might be any kind of iterator, but it's convenient
48 # here if it is just a collection.
49 self.var_list = tuple(var_list)
50
51 def to_object(self):
52 result = super().to_object()
53 result["presentationHint"] = self.hint
54 # How would we know?
55 result["expensive"] = False
56 result["namedVariables"] = len(self.var_list)
57 if self.line is not None:
58 result["line"] = self.line
59 # FIXME construct a Source object
60 return result
61
62 def has_children(self):
63 return True
64
65 def child_count(self):
66 return len(self.var_list)
67
68 @in_gdb_thread
69 def fetch_one_child(self, idx):
70 # Make sure to select the frame first. Ideally this would not
71 # be needed, but this is a way to set the current language
72 # properly so that language-dependent APIs will work.
73 self.inf_frame.select()
74 # Here SYM will conform to the SymValueWrapper interface.
75 sym = self.var_list[idx]
76 name = str(sym.symbol())
77 val = sym.value()
78 if val is None:
79 # No synthetic value, so must read the symbol value
80 # ourselves.
81 val = sym.symbol().value(self.inf_frame)
82 elif not isinstance(val, gdb.Value):
83 val = gdb.Value(val)
84 return (name, val)
85
86
87 class _RegisterReference(_ScopeReference):
88 def __init__(self, name, frame):
89 super().__init__(
90 name, "registers", frame, frame.inferior_frame().architecture().registers()
91 )
92
93 @in_gdb_thread
94 def fetch_one_child(self, idx):
95 return (
96 self.var_list[idx].name,
97 self.inf_frame.read_register(self.var_list[idx]),
98 )
99
100
101 # Helper function to create a DAP scopes for a given frame ID.
102 @in_gdb_thread
103 def _get_scope(id):
104 global frame_to_scope
105 if id in frame_to_scope:
106 scopes = frame_to_scope[id]
107 else:
108 frame = frame_for_id(id)
109 scopes = []
110 args = frame.frame_args()
111 if args:
112 scopes.append(_ScopeReference("Arguments", "arguments", frame, args))
113 locs = frame.frame_locals()
114 if locs:
115 scopes.append(_ScopeReference("Locals", "locals", frame, locs))
116 scopes.append(_RegisterReference("Registers", frame))
117 frame_to_scope[id] = scopes
118 return [x.to_object() for x in scopes]
119
120
121 @request("scopes")
122 def scopes(*, frameId: int, **extra):
123 scopes = send_gdb_with_response(lambda: _get_scope(frameId))
124 return {"scopes": scopes}