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() :
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 (0)
38 {
39 }
40 
41 //----------------------------------------------------------------------
42 // Destructor
43 //----------------------------------------------------------------------
44 SourceManager::~SourceManager()
45 {
46 }
47 
48 size_t
49 SourceManager::DisplaySourceLines
50 (
51     Target *target,
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, target);
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, Target *target)
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, 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     Target *target,
155     const FileSpec &file_spec,
156     uint32_t line,
157     uint32_t context_before,
158     uint32_t context_after,
159     const char* current_line_cstr,
160     Stream *s,
161     const SymbolContextList *bp_locs
162 )
163 {
164     bool same_as_previous = m_last_file_sp && m_last_file_sp->FileSpecMatches (file_spec);
165 
166     if (!same_as_previous)
167         m_last_file_sp = GetFile (file_spec, target);
168 
169     if (line == 0)
170     {
171         if (!same_as_previous)
172             m_last_file_line = 0;
173     }
174 
175     return DisplaySourceLinesWithLineNumbersUsingLastFile (line, context_before, context_after, current_line_cstr, s, bp_locs);
176 }
177 
178 size_t
179 SourceManager::DisplayMoreWithLineNumbers (Stream *s, const SymbolContextList *bp_locs)
180 {
181     if (m_last_file_sp)
182     {
183         if (m_last_file_line == UINT32_MAX)
184             return 0;
185         DisplaySourceLinesWithLineNumbersUsingLastFile (0, m_last_file_context_before, m_last_file_context_after, "", s, bp_locs);
186     }
187     return 0;
188 }
189 
190 
191 
192 SourceManager::File::File(const FileSpec &file_spec, Target *target) :
193     m_file_spec_orig (file_spec),
194     m_file_spec(file_spec),
195     m_mod_time (file_spec.GetModificationTime()),
196     m_data_sp(),
197     m_offsets()
198 {
199     if (!m_mod_time.IsValid())
200     {
201         if (target->GetSourcePathMap().RemapPath(file_spec.GetDirectory(), m_file_spec.GetDirectory()))
202             m_mod_time = file_spec.GetModificationTime();
203     }
204 
205     if (m_mod_time.IsValid())
206         m_data_sp = m_file_spec.ReadFileContents ();
207 }
208 
209 SourceManager::File::~File()
210 {
211 }
212 
213 uint32_t
214 SourceManager::File::GetLineOffset (uint32_t line)
215 {
216     if (line == 0)
217         return UINT32_MAX;
218 
219     if (line == 1)
220         return 0;
221 
222     if (CalculateLineOffsets (line))
223     {
224         if (line < m_offsets.size())
225             return m_offsets[line - 1]; // yes we want "line - 1" in the index
226     }
227     return UINT32_MAX;
228 }
229 
230 bool
231 SourceManager::File::LineIsValid (uint32_t line)
232 {
233     if (line == 0)
234         return false;
235 
236     if (CalculateLineOffsets (line))
237         return line < m_offsets.size();
238     return false;
239 }
240 
241 size_t
242 SourceManager::File::DisplaySourceLines (uint32_t line, uint32_t context_before, uint32_t context_after, Stream *s)
243 {
244     // TODO: use host API to sign up for file modifications to anything in our
245     // source cache and only update when we determine a file has been updated.
246     // For now we check each time we want to display info for the file.
247     TimeValue curr_mod_time (m_file_spec.GetModificationTime());
248     if (m_mod_time != curr_mod_time)
249     {
250         m_mod_time = curr_mod_time;
251         m_data_sp = m_file_spec.ReadFileContents ();
252         m_offsets.clear();
253     }
254 
255     const uint32_t start_line = line <= context_before ? 1 : line - context_before;
256     const uint32_t start_line_offset = GetLineOffset (start_line);
257     if (start_line_offset != UINT32_MAX)
258     {
259         const uint32_t end_line = line + context_after;
260         uint32_t end_line_offset = GetLineOffset (end_line + 1);
261         if (end_line_offset == UINT32_MAX)
262             end_line_offset = m_data_sp->GetByteSize();
263 
264         assert (start_line_offset <= end_line_offset);
265         size_t bytes_written = 0;
266         if (start_line_offset < end_line_offset)
267         {
268             size_t count = end_line_offset - start_line_offset;
269             const uint8_t *cstr = m_data_sp->GetBytes() + start_line_offset;
270             bytes_written = s->Write(cstr, count);
271             if (!is_newline_char(cstr[count-1]))
272                 bytes_written += s->EOL();
273         }
274         return bytes_written;
275     }
276     return 0;
277 }
278 
279 bool
280 SourceManager::File::FileSpecMatches (const FileSpec &file_spec)
281 {
282     return FileSpec::Compare (m_file_spec, file_spec, false) == 0;
283 }
284 
285 
286 bool
287 SourceManager::File::CalculateLineOffsets (uint32_t line)
288 {
289     line = UINT32_MAX;  // TODO: take this line out when we support partial indexing
290     if (line == UINT32_MAX)
291     {
292         // Already done?
293         if (!m_offsets.empty() && m_offsets[0] == UINT32_MAX)
294             return true;
295 
296         if (m_offsets.empty())
297         {
298             if (m_data_sp.get() == NULL)
299                 return false;
300 
301             const char *start = (char *)m_data_sp->GetBytes();
302             if (start)
303             {
304                 const char *end = start + m_data_sp->GetByteSize();
305 
306                 // Calculate all line offsets from scratch
307 
308                 // Push a 1 at index zero to indicate the file has been completely indexed.
309                 m_offsets.push_back(UINT32_MAX);
310                 register const char *s;
311                 for (s = start; s < end; ++s)
312                 {
313                     register char curr_ch = *s;
314                     if (is_newline_char (curr_ch))
315                     {
316                         register char next_ch = s[1];
317                         if (is_newline_char (next_ch))
318                         {
319                             if (curr_ch != next_ch)
320                                 ++s;
321                         }
322                         m_offsets.push_back(s + 1 - start);
323                     }
324                 }
325                 if (!m_offsets.empty())
326                 {
327                     if (m_offsets.back() < end - start)
328                         m_offsets.push_back(end - start);
329                 }
330                 return true;
331             }
332         }
333         else
334         {
335             // Some lines have been populated, start where we last left off
336             assert(!"Not implemented yet");
337         }
338 
339     }
340     else
341     {
342         // Calculate all line offsets up to "line"
343         assert(!"Not implemented yet");
344     }
345     return false;
346 }
347