xref: /llvm-project/lldb/source/Core/SourceManager.cpp (revision 9625d08cec707d87fe7332b9e7344f70c2965699)
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 
19 using namespace lldb_private;
20 
21 static inline bool is_newline_char(char ch)
22 {
23     return ch == '\n' || ch == '\r';
24 }
25 
26 
27 //----------------------------------------------------------------------
28 // SourceManager constructor
29 //----------------------------------------------------------------------
30 SourceManager::SourceManager() :
31     m_file_cache (),
32     m_last_file_sp (),
33     m_last_file_line (0),
34     m_last_file_context_before (0),
35     m_last_file_context_after (0)
36 {
37 }
38 
39 //----------------------------------------------------------------------
40 // Destructor
41 //----------------------------------------------------------------------
42 SourceManager::~SourceManager()
43 {
44 }
45 
46 size_t
47 SourceManager::DisplaySourceLines
48 (
49     const FileSpec &file_spec,
50     uint32_t line,
51     uint32_t context_before,
52     uint32_t context_after,
53     Stream *s
54 )
55 {
56     m_last_file_sp = GetFile (file_spec);
57     m_last_file_line = line + context_after + 1;
58     m_last_file_context_before = context_before;
59     m_last_file_context_after = context_after;
60     if (m_last_file_sp.get())
61         return m_last_file_sp->DisplaySourceLines (line, context_before, context_after, s);
62 
63     return 0;
64 }
65 
66 SourceManager::FileSP
67 SourceManager::GetFile (const FileSpec &file_spec)
68 {
69     FileSP file_sp;
70     FileCache::iterator pos = m_file_cache.find(file_spec);
71     if (pos != m_file_cache.end())
72         file_sp = pos->second;
73     else
74     {
75         file_sp.reset (new File (file_spec));
76         m_file_cache[file_spec] = file_sp;
77     }
78     return file_sp;
79 }
80 
81 size_t
82 SourceManager::DisplaySourceLinesWithLineNumbersUsingLastFile
83 (
84     uint32_t line,
85     uint32_t context_before,
86     uint32_t context_after,
87     const char* current_line_cstr,
88     Stream *s
89 )
90 {
91     if (line == 0)
92     {
93         if (m_last_file_line != 0
94             && m_last_file_line != UINT32_MAX)
95             line = m_last_file_line + context_before;
96         else
97             line = 1;
98     }
99 
100     m_last_file_line = line + context_after + 1;
101     m_last_file_context_before = context_before;
102     m_last_file_context_after = context_after;
103 
104     if (context_before == UINT32_MAX)
105         context_before = 0;
106     if (context_after == UINT32_MAX)
107         context_after = 10;
108 
109     if (m_last_file_sp.get())
110     {
111         const uint32_t start_line = line <= context_before ? 1 : line - context_before;
112         const uint32_t end_line = line + context_after;
113         uint32_t curr_line;
114         for (curr_line = start_line; curr_line <= end_line; ++curr_line)
115         {
116             if (!m_last_file_sp->LineIsValid (curr_line))
117             {
118                 m_last_file_line = UINT32_MAX;
119                 break;
120             }
121 
122             s->Printf("%4u %2.2s\t", curr_line, curr_line == line ? current_line_cstr : "");
123             if (m_last_file_sp->DisplaySourceLines (curr_line, 0, 0, s) == 0)
124             {
125                 m_last_file_line = UINT32_MAX;
126                 break;
127             }
128         }
129     }
130     return 0;
131 }
132 
133 size_t
134 SourceManager::DisplaySourceLinesWithLineNumbers
135 (
136     const FileSpec &file_spec,
137     uint32_t line,
138     uint32_t context_before,
139     uint32_t context_after,
140     const char* current_line_cstr,
141     Stream *s
142 )
143 {
144     bool same_as_previous = m_last_file_sp && m_last_file_sp->FileSpecMatches (file_spec);
145 
146     if (!same_as_previous)
147         m_last_file_sp = GetFile (file_spec);
148 
149     if (line == 0)
150     {
151         if (!same_as_previous)
152             m_last_file_line = 0;
153     }
154 
155     return DisplaySourceLinesWithLineNumbersUsingLastFile (line, context_before, context_after, current_line_cstr, s);
156 }
157 
158 size_t
159 SourceManager::DisplayMoreWithLineNumbers (Stream *s)
160 {
161     if (m_last_file_sp)
162     {
163         if (m_last_file_line == UINT32_MAX)
164             return 0;
165         DisplaySourceLinesWithLineNumbersUsingLastFile (0, m_last_file_context_before, m_last_file_context_after, "", s);
166     }
167     return 0;
168 }
169 
170 
171 
172 SourceManager::File::File(const FileSpec &file_spec) :
173     m_file_spec(file_spec),
174     m_mod_time (m_file_spec.GetModificationTime()),
175     m_data_sp(file_spec.ReadFileContents ()),
176     m_offsets()
177 {
178 }
179 
180 SourceManager::File::~File()
181 {
182 }
183 
184 uint32_t
185 SourceManager::File::GetLineOffset (uint32_t line)
186 {
187     if (line == 0)
188         return UINT32_MAX;
189 
190     if (line == 1)
191         return 0;
192 
193     if (CalculateLineOffsets (line))
194     {
195         if (line < m_offsets.size())
196             return m_offsets[line - 1]; // yes we want "line - 1" in the index
197     }
198     return UINT32_MAX;
199 }
200 
201 bool
202 SourceManager::File::LineIsValid (uint32_t line)
203 {
204     if (line == 0)
205         return false;
206 
207     if (CalculateLineOffsets (line))
208         return line < m_offsets.size();
209     return false;
210 }
211 
212 size_t
213 SourceManager::File::DisplaySourceLines (uint32_t line, uint32_t context_before, uint32_t context_after, Stream *s)
214 {
215     // TODO: use host API to sign up for file modifications to anything in our
216     // source cache and only update when we determine a file has been updated.
217     // For now we check each time we want to display info for the file.
218     TimeValue curr_mod_time (m_file_spec.GetModificationTime());
219     if (m_mod_time != curr_mod_time)
220     {
221         m_mod_time = curr_mod_time;
222         m_data_sp = m_file_spec.ReadFileContents ();
223         m_offsets.clear();
224     }
225 
226     const uint32_t start_line = line <= context_before ? 1 : line - context_before;
227     const uint32_t start_line_offset = GetLineOffset (start_line);
228     if (start_line_offset != UINT32_MAX)
229     {
230         const uint32_t end_line = line + context_after;
231         uint32_t end_line_offset = GetLineOffset (end_line + 1);
232         if (end_line_offset == UINT32_MAX)
233             end_line_offset = m_data_sp->GetByteSize();
234 
235         assert (start_line_offset <= end_line_offset);
236         size_t bytes_written = 0;
237         if (start_line_offset < end_line_offset)
238         {
239             size_t count = end_line_offset - start_line_offset;
240             const uint8_t *cstr = m_data_sp->GetBytes() + start_line_offset;
241             bytes_written = s->Write(cstr, count);
242             if (!is_newline_char(cstr[count-1]))
243                 bytes_written += s->EOL();
244         }
245         return bytes_written;
246     }
247     return 0;
248 }
249 
250 bool
251 SourceManager::File::FileSpecMatches (const FileSpec &file_spec)
252 {
253     return FileSpec::Compare (m_file_spec, file_spec, false) == 0;
254 }
255 
256 
257 bool
258 SourceManager::File::CalculateLineOffsets (uint32_t line)
259 {
260     line = UINT32_MAX;  // TODO: take this line out when we support partial indexing
261     if (line == UINT32_MAX)
262     {
263         // Already done?
264         if (!m_offsets.empty() && m_offsets[0] == UINT32_MAX)
265             return true;
266 
267         if (m_offsets.empty())
268         {
269             if (m_data_sp.get() == NULL)
270                 return false;
271 
272             const char *start = (char *)m_data_sp->GetBytes();
273             if (start)
274             {
275                 const char *end = start + m_data_sp->GetByteSize();
276 
277                 // Calculate all line offsets from scratch
278 
279                 // Push a 1 at index zero to indicate the file has been completely indexed.
280                 m_offsets.push_back(UINT32_MAX);
281                 register const char *s;
282                 for (s = start; s < end; ++s)
283                 {
284                     register char curr_ch = *s;
285                     if (is_newline_char (curr_ch))
286                     {
287                         register char next_ch = s[1];
288                         if (is_newline_char (next_ch))
289                         {
290                             if (curr_ch != next_ch)
291                                 ++s;
292                         }
293                         m_offsets.push_back(s + 1 - start);
294                     }
295                 }
296                 if (!m_offsets.empty())
297                 {
298                     if (m_offsets.back() < end - start)
299                         m_offsets.push_back(end - start);
300                 }
301                 return true;
302             }
303         }
304         else
305         {
306             // Some lines have been populated, start where we last left off
307             assert(!"Not implemented yet");
308         }
309 
310     }
311     else
312     {
313         // Calculate all line offsets up to "line"
314         assert(!"Not implemented yet");
315     }
316     return false;
317 }
318