xref: /llvm-project/lldb/source/Core/SourceManager.cpp (revision 30fdc8d841c9d24ac5f3d452b6ece84ee0ac991c)
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_data_sp(file_spec.ReadFileContents ()),
175     m_offsets()
176 {
177 }
178 
179 SourceManager::File::~File()
180 {
181 }
182 
183 uint32_t
184 SourceManager::File::GetLineOffset (uint32_t line)
185 {
186     if (line == 0)
187         return UINT32_MAX;
188 
189     if (line == 1)
190         return 0;
191 
192     if (CalculateLineOffsets (line))
193     {
194         if (line < m_offsets.size())
195             return m_offsets[line - 1]; // yes we want "line - 1" in the index
196     }
197     return UINT32_MAX;
198 }
199 
200 bool
201 SourceManager::File::LineIsValid (uint32_t line)
202 {
203     if (line == 0)
204         return false;
205 
206     if (CalculateLineOffsets (line))
207         return line < m_offsets.size();
208     return false;
209 }
210 
211 size_t
212 SourceManager::File::DisplaySourceLines (uint32_t line, uint32_t context_before, uint32_t context_after, Stream *s)
213 {
214     const uint32_t start_line = line <= context_before ? 1 : line - context_before;
215     const uint32_t start_line_offset = GetLineOffset (start_line);
216     if (start_line_offset != UINT32_MAX)
217     {
218         const uint32_t end_line = line + context_after;
219         uint32_t end_line_offset = GetLineOffset (end_line + 1);
220         if (end_line_offset == UINT32_MAX)
221             end_line_offset = m_data_sp->GetByteSize();
222 
223         assert (start_line_offset <= end_line_offset);
224         size_t bytes_written = 0;
225         if (start_line_offset < end_line_offset)
226         {
227             size_t count = end_line_offset - start_line_offset;
228             const uint8_t *cstr = m_data_sp->GetBytes() + start_line_offset;
229             bytes_written = s->Write(cstr, count);
230             if (!is_newline_char(cstr[count-1]))
231                 bytes_written += s->EOL();
232         }
233         return bytes_written;
234     }
235     return 0;
236 }
237 
238 bool
239 SourceManager::File::FileSpecMatches (const FileSpec &file_spec)
240 {
241     return FileSpec::Compare (m_file_spec, file_spec, false) == 0;
242 }
243 
244 
245 bool
246 SourceManager::File::CalculateLineOffsets (uint32_t line)
247 {
248     line = UINT32_MAX;  // TODO: take this line out when we support partial indexing
249     if (line == UINT32_MAX)
250     {
251         // Already done?
252         if (!m_offsets.empty() && m_offsets[0] == UINT32_MAX)
253             return true;
254 
255         if (m_offsets.empty())
256         {
257             if (m_data_sp.get() == NULL)
258                 return false;
259 
260             const char *start = (char *)m_data_sp->GetBytes();
261             if (start)
262             {
263                 const char *end = start + m_data_sp->GetByteSize();
264 
265                 // Calculate all line offsets from scratch
266 
267                 // Push a 1 at index zero to indicate the file has been completely indexed.
268                 m_offsets.push_back(UINT32_MAX);
269                 register const char *s;
270                 for (s = start; s < end; ++s)
271                 {
272                     register char curr_ch = *s;
273                     if (is_newline_char (curr_ch))
274                     {
275                         register char next_ch = s[1];
276                         if (is_newline_char (next_ch))
277                         {
278                             if (curr_ch != next_ch)
279                                 ++s;
280                         }
281                         m_offsets.push_back(s + 1 - start);
282                     }
283                 }
284                 if (!m_offsets.empty())
285                 {
286                     if (m_offsets.back() < end - start)
287                         m_offsets.push_back(end - start);
288                 }
289                 return true;
290             }
291         }
292         else
293         {
294             // Some lines have been populated, start where we last left off
295             assert(!"Not implemented yet");
296         }
297 
298     }
299     else
300     {
301         // Calculate all line offsets up to "line"
302         assert(!"Not implemented yet");
303     }
304     return false;
305 }
306