]> Git Repo - binutils.git/blob - gdb/source-cache.c
gdb: move go_language class declaration into header file
[binutils.git] / gdb / source-cache.c
1 /* Cache of styled source file text
2    Copyright (C) 2018-2020 Free Software Foundation, Inc.
3
4    This file is part of GDB.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19 #include "defs.h"
20 #include "source-cache.h"
21 #include "gdbsupport/scoped_fd.h"
22 #include "source.h"
23 #include "cli/cli-style.h"
24 #include "symtab.h"
25 #include "gdbsupport/selftest.h"
26 #include "objfiles.h"
27 #include "exec.h"
28
29 #ifdef HAVE_SOURCE_HIGHLIGHT
30 /* If Gnulib redirects 'open' and 'close' to its replacements
31    'rpl_open' and 'rpl_close' via cpp macros, including <fstream>
32    below with those macros in effect will cause unresolved externals
33    when GDB is linked.  Happens, e.g., in the MinGW build.  */
34 #undef open
35 #undef close
36 #include <sstream>
37 #include <srchilite/sourcehighlight.h>
38 #include <srchilite/langmap.h>
39 #endif
40
41 /* The number of source files we'll cache.  */
42
43 #define MAX_ENTRIES 5
44
45 /* See source-cache.h.  */
46
47 source_cache g_source_cache;
48
49 /* See source-cache.h.  */
50
51 std::string
52 source_cache::get_plain_source_lines (struct symtab *s,
53                                       const std::string &fullname)
54 {
55   scoped_fd desc (open_source_file (s));
56   if (desc.get () < 0)
57     perror_with_name (symtab_to_filename_for_display (s));
58
59   struct stat st;
60   if (fstat (desc.get (), &st) < 0)
61     perror_with_name (symtab_to_filename_for_display (s));
62
63   std::string lines;
64   lines.resize (st.st_size);
65   if (myread (desc.get (), &lines[0], lines.size ()) < 0)
66     perror_with_name (symtab_to_filename_for_display (s));
67
68   time_t mtime = 0;
69   if (SYMTAB_OBJFILE (s) != NULL && SYMTAB_OBJFILE (s)->obfd != NULL)
70     mtime = SYMTAB_OBJFILE (s)->mtime;
71   else if (current_program_space->exec_bfd ())
72     mtime = current_program_space->ebfd_mtime;
73
74   if (mtime && mtime < st.st_mtime)
75     warning (_("Source file is more recent than executable."));
76
77   std::vector<off_t> offsets;
78   offsets.push_back (0);
79   for (size_t offset = lines.find ('\n');
80        offset != std::string::npos;
81        offset = lines.find ('\n', offset))
82     {
83       ++offset;
84       /* A newline at the end does not start a new line.  It would
85          seem simpler to just strip the newline in this function, but
86          then "list" won't print the final newline.  */
87       if (offset != lines.size ())
88         offsets.push_back (offset);
89     }
90
91   offsets.shrink_to_fit ();
92   m_offset_cache.emplace (fullname, std::move (offsets));
93
94   return lines;
95 }
96
97 #ifdef HAVE_SOURCE_HIGHLIGHT
98
99 /* Return the Source Highlight language name, given a gdb language
100    LANG.  Returns NULL if the language is not known.  */
101
102 static const char *
103 get_language_name (enum language lang)
104 {
105   switch (lang)
106     {
107     case language_c:
108     case language_objc:
109       return "c.lang";
110
111     case language_cplus:
112       return "cpp.lang";
113
114     case language_d:
115       return "d.lang";
116
117     case language_go:
118       return "go.lang";
119
120     case language_fortran:
121       return "fortran.lang";
122
123     case language_m2:
124       /* Not handled by Source Highlight.  */
125       break;
126
127     case language_asm:
128       return "asm.lang";
129
130     case language_pascal:
131       return "pascal.lang";
132
133     case language_opencl:
134       /* Not handled by Source Highlight.  */
135       break;
136
137     case language_rust:
138       return "rust.lang";
139
140     case language_ada:
141       return "ada.lang";
142
143     default:
144       break;
145     }
146
147   return nullptr;
148 }
149
150 #endif /* HAVE_SOURCE_HIGHLIGHT */
151
152 /* See source-cache.h.  */
153
154 bool
155 source_cache::ensure (struct symtab *s)
156 {
157   std::string fullname = symtab_to_fullname (s);
158
159   size_t size = m_source_map.size ();
160   for (int i = 0; i < size; ++i)
161     {
162       if (m_source_map[i].fullname == fullname)
163         {
164           /* This should always hold, because we create the file
165              offsets when reading the file, and never free them
166              without also clearing the contents cache.  */
167           gdb_assert (m_offset_cache.find (fullname)
168                       != m_offset_cache.end ());
169           /* Not strictly LRU, but at least ensure that the most
170              recently used entry is always the last candidate for
171              deletion.  Note that this property is relied upon by at
172              least one caller.  */
173           if (i != size - 1)
174             std::swap (m_source_map[i], m_source_map[size - 1]);
175           return true;
176         }
177     }
178
179   std::string contents;
180   try
181     {
182       contents = get_plain_source_lines (s, fullname);
183     }
184   catch (const gdb_exception_error &e)
185     {
186       /* If 's' is not found, an exception is thrown.  */
187       return false;
188     }
189
190   if (source_styling && gdb_stdout->can_emit_style_escape ())
191     {
192 #ifdef HAVE_SOURCE_HIGHLIGHT
193       bool already_styled = false;
194       const char *lang_name = get_language_name (SYMTAB_LANGUAGE (s));
195       if (lang_name != nullptr)
196         {
197           /* The global source highlight object, or null if one was
198              never constructed.  This is stored here rather than in
199              the class so that we don't need to include anything or do
200              conditional compilation in source-cache.h.  */
201           static srchilite::SourceHighlight *highlighter;
202
203           try
204             {
205               if (highlighter == nullptr)
206                 {
207                   highlighter = new srchilite::SourceHighlight ("esc.outlang");
208                   highlighter->setStyleFile ("esc.style");
209                 }
210
211               std::istringstream input (contents);
212               std::ostringstream output;
213               highlighter->highlight (input, output, lang_name, fullname);
214               contents = output.str ();
215               already_styled = true;
216             }
217           catch (...)
218             {
219               /* Source Highlight will throw an exception if
220                  highlighting fails.  One possible reason it can fail
221                  is if the language is unknown -- which matters to gdb
222                  because Rust support wasn't added until after 3.1.8.
223                  Ignore exceptions here and fall back to
224                  un-highlighted text. */
225             }
226         }
227
228       if (!already_styled)
229 #endif /* HAVE_SOURCE_HIGHLIGHT */
230         {
231           gdb::optional<std::string> ext_contents;
232           ext_contents = ext_lang_colorize (fullname, contents);
233           if (ext_contents.has_value ())
234             contents = std::move (*ext_contents);
235         }
236     }
237
238   source_text result = { std::move (fullname), std::move (contents) };
239   m_source_map.push_back (std::move (result));
240
241   if (m_source_map.size () > MAX_ENTRIES)
242     m_source_map.erase (m_source_map.begin ());
243
244   return true;
245 }
246
247 /* See source-cache.h.  */
248
249 bool
250 source_cache::get_line_charpos (struct symtab *s,
251                                 const std::vector<off_t> **offsets)
252 {
253   std::string fullname = symtab_to_fullname (s);
254
255   auto iter = m_offset_cache.find (fullname);
256   if (iter == m_offset_cache.end ())
257     {
258       if (!ensure (s))
259         return false;
260       iter = m_offset_cache.find (fullname);
261       /* cache_source_text ensured this was entered.  */
262       gdb_assert (iter != m_offset_cache.end ());
263     }
264
265   *offsets = &iter->second;
266   return true;
267 }
268
269 /* A helper function that extracts the desired source lines from TEXT,
270    putting them into LINES_OUT.  The arguments are as for
271    get_source_lines.  Returns true on success, false if the line
272    numbers are invalid.  */
273
274 static bool
275 extract_lines (const std::string &text, int first_line, int last_line,
276                std::string *lines_out)
277 {
278   int lineno = 1;
279   std::string::size_type pos = 0;
280   std::string::size_type first_pos = std::string::npos;
281
282   while (pos != std::string::npos && lineno <= last_line)
283     {
284       std::string::size_type new_pos = text.find ('\n', pos);
285
286       if (lineno == first_line)
287         first_pos = pos;
288
289       pos = new_pos;
290       if (lineno == last_line || pos == std::string::npos)
291         {
292           /* A newline at the end does not start a new line.  */
293           if (first_pos == std::string::npos
294               || first_pos == text.size ())
295             return false;
296           if (pos == std::string::npos)
297             pos = text.size ();
298           else
299             ++pos;
300           *lines_out = text.substr (first_pos, pos - first_pos);
301           return true;
302         }
303       ++lineno;
304       ++pos;
305     }
306
307   return false;
308 }
309
310 /* See source-cache.h.  */
311
312 bool
313 source_cache::get_source_lines (struct symtab *s, int first_line,
314                                 int last_line, std::string *lines)
315 {
316   if (first_line < 1 || last_line < 1 || first_line > last_line)
317     return false;
318
319   if (!ensure (s))
320     return false;
321
322   return extract_lines (m_source_map.back ().contents,
323                         first_line, last_line, lines);
324 }
325
326 #if GDB_SELF_TEST
327 namespace selftests
328 {
329 static void extract_lines_test ()
330 {
331   std::string input_text = "abc\ndef\nghi\njkl\n";
332   std::string result;
333
334   SELF_CHECK (extract_lines (input_text, 1, 1, &result)
335               && result == "abc\n");
336   SELF_CHECK (!extract_lines (input_text, 2, 1, &result));
337   SELF_CHECK (extract_lines (input_text, 1, 2, &result)
338               && result == "abc\ndef\n");
339   SELF_CHECK (extract_lines ("abc", 1, 1, &result)
340               && result == "abc");
341 }
342 }
343 #endif
344
345 void _initialize_source_cache ();
346 void
347 _initialize_source_cache ()
348 {
349 #if GDB_SELF_TEST
350   selftests::register_test ("source-cache", selftests::extract_lines_test);
351 #endif
352 }
This page took 0.046681 seconds and 4 git commands to generate.