xref: /llvm-project/lldb/source/Core/SourceManager.cpp (revision b7f6b2fa3c4a5162223850b28e63b5193159ae6d)
1 //===-- SourceManager.cpp ---------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "lldb/Core/SourceManager.h"
11 
12 // C Includes
13 // C++ Includes
14 // Other libraries and framework includes
15 // Project includes
16 #include "lldb/Core/DataBuffer.h"
17 #include "lldb/Core/Stream.h"
18 #include "lldb/Symbol/SymbolContext.h"
19 #include "lldb/Target/Target.h"
20 
21 using namespace lldb_private;
22 
23 static inline bool is_newline_char(char ch)
24 {
25     return ch == '\n' || ch == '\r';
26 }
27 
28 
29 //----------------------------------------------------------------------
30 // SourceManager constructor
31 //----------------------------------------------------------------------
32 SourceManager::SourceManager(Target *target) :
33     m_file_cache (),
34     m_last_file_sp (),
35     m_last_file_line (0),
36     m_last_file_context_before (0),
37     m_last_file_context_after (10),
38     m_target (target)
39 {
40 }
41 
42 //----------------------------------------------------------------------
43 // Destructor
44 //----------------------------------------------------------------------
45 SourceManager::~SourceManager()
46 {
47 }
48 
49 size_t
50 SourceManager::DisplaySourceLines
51 (
52     const FileSpec &file_spec,
53     uint32_t line,
54     uint32_t context_before,
55     uint32_t context_after,
56     Stream *s
57 )
58 {
59     m_last_file_sp = GetFile (file_spec);
60     m_last_file_line = line + context_after + 1;
61     m_last_file_context_before = context_before;
62     m_last_file_context_after = context_after;
63     if (m_last_file_sp.get())
64         return m_last_file_sp->DisplaySourceLines (line, context_before, context_after, s);
65 
66     return 0;
67 }
68 
69 SourceManager::FileSP
70 SourceManager::GetFile (const FileSpec &file_spec)
71 {
72     FileSP file_sp;
73     FileCache::iterator pos = m_file_cache.find(file_spec);
74     if (pos != m_file_cache.end())
75         file_sp = pos->second;
76     else
77     {
78         file_sp.reset (new File (file_spec, m_target));
79         m_file_cache[file_spec] = file_sp;
80     }
81     return file_sp;
82 }
83 
84 size_t
85 SourceManager::DisplaySourceLinesWithLineNumbersUsingLastFile
86 (
87     uint32_t line,
88     uint32_t context_before,
89     uint32_t context_after,
90     const char* current_line_cstr,
91     Stream *s,
92     const SymbolContextList *bp_locs
93 )
94 {
95     if (line == 0)
96     {
97         if (m_last_file_line != 0
98             && m_last_file_line != UINT32_MAX)
99             line = m_last_file_line + context_before;
100         else
101             line = 1;
102     }
103 
104     m_last_file_line = line + context_after + 1;
105     m_last_file_context_before = context_before;
106     m_last_file_context_after = context_after;
107 
108     if (context_before == UINT32_MAX)
109         context_before = 0;
110     if (context_after == UINT32_MAX)
111         context_after = 10;
112 
113     if (m_last_file_sp.get())
114     {
115         const uint32_t start_line = line <= context_before ? 1 : line - context_before;
116         const uint32_t end_line = line + context_after;
117         uint32_t curr_line;
118         for (curr_line = start_line; curr_line <= end_line; ++curr_line)
119         {
120             if (!m_last_file_sp->LineIsValid (curr_line))
121             {
122                 m_last_file_line = UINT32_MAX;
123                 break;
124             }
125 
126             char prefix[32] = "";
127             if (bp_locs)
128             {
129                 uint32_t bp_count = bp_locs->NumLineEntriesWithLine (curr_line);
130 
131                 if (bp_count > 0)
132                     ::snprintf (prefix, sizeof (prefix), "[%u] ", bp_count);
133                 else
134                     ::snprintf (prefix, sizeof (prefix), "    ");
135             }
136 
137             s->Printf("%s%2.2s %-4u\t",
138                       prefix,
139                       curr_line == line ? current_line_cstr : "",
140                       curr_line);
141             if (m_last_file_sp->DisplaySourceLines (curr_line, 0, 0, s) == 0)
142             {
143                 m_last_file_line = UINT32_MAX;
144                 break;
145             }
146         }
147     }
148     return 0;
149 }
150 
151 size_t
152 SourceManager::DisplaySourceLinesWithLineNumbers
153 (
154     const FileSpec &file_spec,
155     uint32_t line,
156     uint32_t context_before,
157     uint32_t context_after,
158     const char* current_line_cstr,
159     Stream *s,
160     const SymbolContextList *bp_locs
161 )
162 {
163     bool same_as_previous = m_last_file_sp && m_last_file_sp->FileSpecMatches (file_spec);
164 
165     if (!same_as_previous)
166         m_last_file_sp = GetFile (file_spec);
167 
168     if (line == 0)
169     {
170         if (!same_as_previous)
171             m_last_file_line = 0;
172     }
173 
174     return DisplaySourceLinesWithLineNumbersUsingLastFile (line, context_before, context_after, current_line_cstr, s, bp_locs);
175 }
176 
177 size_t
178 SourceManager::DisplayMoreWithLineNumbers (Stream *s, const SymbolContextList *bp_locs)
179 {
180     if (m_last_file_sp)
181     {
182         if (m_last_file_line == UINT32_MAX)
183             return 0;
184         DisplaySourceLinesWithLineNumbersUsingLastFile (0, m_last_file_context_before, m_last_file_context_after, "", s, bp_locs);
185     }
186     return 0;
187 }
188 
189 bool
190 SourceManager::SetDefaultFileAndLine (const FileSpec &file_spec, uint32_t line)
191 {
192     FileSP old_file_sp = m_last_file_sp;
193     m_last_file_sp = GetFile (file_spec);
194     if (m_last_file_sp)
195     {
196         m_last_file_line = line;
197         return true;
198     }
199     else
200     {
201         m_last_file_sp = old_file_sp;
202         return false;
203     }
204 }
205 
206 bool
207 SourceManager::GetDefaultFileAndLine (FileSpec &file_spec, uint32_t &line)
208 {
209     if (m_last_file_sp)
210     {
211         file_spec = m_last_file_sp->GetFileSpec();
212         line = m_last_file_line;
213         return true;
214     }
215     else
216         return false;
217 }
218 
219 
220 SourceManager::File::File(const FileSpec &file_spec, Target *target) :
221     m_file_spec_orig (file_spec),
222     m_file_spec(file_spec),
223     m_mod_time (file_spec.GetModificationTime()),
224     m_data_sp(),
225     m_offsets()
226 {
227     if (!m_mod_time.IsValid())
228     {
229         if (target && target->GetSourcePathMap().RemapPath(file_spec.GetDirectory(), m_file_spec.GetDirectory()))
230             m_mod_time = file_spec.GetModificationTime();
231     }
232 
233     if (m_mod_time.IsValid())
234         m_data_sp = m_file_spec.ReadFileContents ();
235 }
236 
237 SourceManager::File::~File()
238 {
239 }
240 
241 uint32_t
242 SourceManager::File::GetLineOffset (uint32_t line)
243 {
244     if (line == 0)
245         return UINT32_MAX;
246 
247     if (line == 1)
248         return 0;
249 
250     if (CalculateLineOffsets (line))
251     {
252         if (line < m_offsets.size())
253             return m_offsets[line - 1]; // yes we want "line - 1" in the index
254     }
255     return UINT32_MAX;
256 }
257 
258 bool
259 SourceManager::File::LineIsValid (uint32_t line)
260 {
261     if (line == 0)
262         return false;
263 
264     if (CalculateLineOffsets (line))
265         return line < m_offsets.size();
266     return false;
267 }
268 
269 size_t
270 SourceManager::File::DisplaySourceLines (uint32_t line, uint32_t context_before, uint32_t context_after, Stream *s)
271 {
272     // TODO: use host API to sign up for file modifications to anything in our
273     // source cache and only update when we determine a file has been updated.
274     // For now we check each time we want to display info for the file.
275     TimeValue curr_mod_time (m_file_spec.GetModificationTime());
276     if (m_mod_time != curr_mod_time)
277     {
278         m_mod_time = curr_mod_time;
279         m_data_sp = m_file_spec.ReadFileContents ();
280         m_offsets.clear();
281     }
282 
283     const uint32_t start_line = line <= context_before ? 1 : line - context_before;
284     const uint32_t start_line_offset = GetLineOffset (start_line);
285     if (start_line_offset != UINT32_MAX)
286     {
287         const uint32_t end_line = line + context_after;
288         uint32_t end_line_offset = GetLineOffset (end_line + 1);
289         if (end_line_offset == UINT32_MAX)
290             end_line_offset = m_data_sp->GetByteSize();
291 
292         assert (start_line_offset <= end_line_offset);
293         size_t bytes_written = 0;
294         if (start_line_offset < end_line_offset)
295         {
296             size_t count = end_line_offset - start_line_offset;
297             const uint8_t *cstr = m_data_sp->GetBytes() + start_line_offset;
298             bytes_written = s->Write(cstr, count);
299             if (!is_newline_char(cstr[count-1]))
300                 bytes_written += s->EOL();
301         }
302         return bytes_written;
303     }
304     return 0;
305 }
306 
307 bool
308 SourceManager::File::FileSpecMatches (const FileSpec &file_spec)
309 {
310     return FileSpec::Equal (m_file_spec, file_spec, false);
311 }
312 
313 
314 bool
315 SourceManager::File::CalculateLineOffsets (uint32_t line)
316 {
317     line = UINT32_MAX;  // TODO: take this line out when we support partial indexing
318     if (line == UINT32_MAX)
319     {
320         // Already done?
321         if (!m_offsets.empty() && m_offsets[0] == UINT32_MAX)
322             return true;
323 
324         if (m_offsets.empty())
325         {
326             if (m_data_sp.get() == NULL)
327                 return false;
328 
329             const char *start = (char *)m_data_sp->GetBytes();
330             if (start)
331             {
332                 const char *end = start + m_data_sp->GetByteSize();
333 
334                 // Calculate all line offsets from scratch
335 
336                 // Push a 1 at index zero to indicate the file has been completely indexed.
337                 m_offsets.push_back(UINT32_MAX);
338                 register const char *s;
339                 for (s = start; s < end; ++s)
340                 {
341                     register char curr_ch = *s;
342                     if (is_newline_char (curr_ch))
343                     {
344                         register char next_ch = s[1];
345                         if (is_newline_char (next_ch))
346                         {
347                             if (curr_ch != next_ch)
348                                 ++s;
349                         }
350                         m_offsets.push_back(s + 1 - start);
351                     }
352                 }
353                 if (!m_offsets.empty())
354                 {
355                     if (m_offsets.back() < end - start)
356                         m_offsets.push_back(end - start);
357                 }
358                 return true;
359             }
360         }
361         else
362         {
363             // Some lines have been populated, start where we last left off
364             assert(!"Not implemented yet");
365         }
366 
367     }
368     else
369     {
370         // Calculate all line offsets up to "line"
371         assert(!"Not implemented yet");
372     }
373     return false;
374 }
375