]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - gdb/python/lib/gdb/dap/sources.py
efcd7995e4c4240d990634d813b6c3bcac916711
[thirdparty/binutils-gdb.git] / gdb / python / lib / gdb / dap / sources.py
1 # Copyright 2023-2025 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 os
17
18 from .server import capability, request
19 from .startup import DAPException, exec_mi_and_log, in_gdb_thread
20
21 # The next available source reference ID. Must be greater than 0.
22 _next_source = 1
23
24 # Map from full paths to Source dictionaries.
25 _source_map = {}
26
27 # Map from a source reference ID back to the same Source that is
28 # stored in _source_map.
29 _id_map = {}
30
31
32 @in_gdb_thread
33 def make_source(fullname, filename=None):
34 """Return the Source for a given file name.
35
36 FULLNAME is the full name. This is used as the key.
37 FILENAME is the base name; if None (the default), then it is
38 computed from FULLNAME.
39 """
40 if fullname in _source_map:
41 result = _source_map[fullname]
42 else:
43 if filename is None:
44 filename = os.path.basename(fullname)
45
46 result = {
47 "name": filename,
48 "path": fullname,
49 }
50
51 if not os.path.exists(fullname):
52 global _next_source
53 result["sourceReference"] = _next_source
54
55 _id_map[_next_source] = result
56 _next_source += 1
57
58 _source_map[fullname] = result
59 return result
60
61
62 @in_gdb_thread
63 def decode_source(source):
64 """Decode a Source object.
65
66 Finds and returns the filename of a given Source object."""
67 if "sourceReference" not in source or source["sourceReference"] <= 0:
68 if "path" in source:
69 return source["path"]
70 raise DAPException("either 'path' or 'sourceReference' must appear in Source")
71 ref = source["sourceReference"]
72 if ref not in _id_map:
73 raise DAPException("no sourceReference " + str(ref))
74 return _id_map[ref]["path"]
75
76
77 @request("loadedSources")
78 @capability("supportsLoadedSourcesRequest")
79 def loaded_sources(**extra):
80 result = []
81 for elt in exec_mi_and_log("-file-list-exec-source-files")["files"]:
82 result.append(make_source(elt["fullname"], elt["file"]))
83 return {
84 "sources": result,
85 }
86
87
88 @request("source")
89 def source(*, source=None, sourceReference: int, **extra):
90 # The 'sourceReference' parameter is required by the spec, but is
91 # for backward compatibility, which I take to mean that the
92 # 'source' is preferred.
93 if source is None:
94 source = {"sourceReference": sourceReference}
95 filename = decode_source(source)
96 with open(filename) as f:
97 content = f.read()
98 return {
99 "content": content,
100 }