]>
Commit | Line | Data |
---|---|---|
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 | } |