blob: e769625051d9d3adc4f8da30a05bd2693300c011 [file] [log] [blame]
Chris Lattner24943d22010-06-08 16:52:24 +00001//===-- 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
Daniel Malead891f9b2012-12-05 00:20:57 +000010#include "lldb/lldb-python.h"
11
Chris Lattner24943d22010-06-08 16:52:24 +000012#include "lldb/Core/SourceManager.h"
13
14// C Includes
15// C++ Includes
16// Other libraries and framework includes
17// Project includes
18#include "lldb/Core/DataBuffer.h"
Jim Inghamcc637462011-09-13 00:29:56 +000019#include "lldb/Core/Debugger.h"
Greg Clayton49ce8962012-08-29 21:13:06 +000020#include "lldb/Core/Module.h"
Chris Lattner24943d22010-06-08 16:52:24 +000021#include "lldb/Core/Stream.h"
Sean Callanan3e80cd92011-10-12 02:08:07 +000022#include "lldb/Symbol/ClangNamespaceDecl.h"
Greg Clayton49ce8962012-08-29 21:13:06 +000023#include "lldb/Symbol/CompileUnit.h"
24#include "lldb/Symbol/Function.h"
Greg Clayton52c8b6e2011-04-19 04:19:37 +000025#include "lldb/Symbol/SymbolContext.h"
Greg Claytonff44ab42011-04-23 02:04:55 +000026#include "lldb/Target/Target.h"
Chris Lattner24943d22010-06-08 16:52:24 +000027
28using namespace lldb_private;
29
30static inline bool is_newline_char(char ch)
31{
32 return ch == '\n' || ch == '\r';
33}
34
35
36//----------------------------------------------------------------------
37// SourceManager constructor
38//----------------------------------------------------------------------
Jim Inghamcc637462011-09-13 00:29:56 +000039SourceManager::SourceManager(Target &target) :
Chris Lattner24943d22010-06-08 16:52:24 +000040 m_last_file_sp (),
41 m_last_file_line (0),
42 m_last_file_context_before (0),
Jim Inghamfdf24ef2011-09-08 22:13:49 +000043 m_last_file_context_after (10),
Jim Inghamca804bf2011-10-07 22:16:04 +000044 m_default_set(false),
Jim Inghamcc637462011-09-13 00:29:56 +000045 m_target (&target),
46 m_debugger(NULL)
47{
48 m_debugger = &(m_target->GetDebugger());
49}
50
51SourceManager::SourceManager(Debugger &debugger) :
52 m_last_file_sp (),
53 m_last_file_line (0),
54 m_last_file_context_before (0),
55 m_last_file_context_after (10),
Jim Inghamca804bf2011-10-07 22:16:04 +000056 m_default_set(false),
Jim Inghamcc637462011-09-13 00:29:56 +000057 m_target (NULL),
58 m_debugger (&debugger)
Chris Lattner24943d22010-06-08 16:52:24 +000059{
60}
61
62//----------------------------------------------------------------------
63// Destructor
64//----------------------------------------------------------------------
65SourceManager::~SourceManager()
66{
67}
68
69size_t
70SourceManager::DisplaySourceLines
71(
72 const FileSpec &file_spec,
73 uint32_t line,
74 uint32_t context_before,
75 uint32_t context_after,
76 Stream *s
77)
78{
Jim Inghamfdf24ef2011-09-08 22:13:49 +000079 m_last_file_sp = GetFile (file_spec);
Chris Lattner24943d22010-06-08 16:52:24 +000080 m_last_file_line = line + context_after + 1;
81 m_last_file_context_before = context_before;
82 m_last_file_context_after = context_after;
83 if (m_last_file_sp.get())
84 return m_last_file_sp->DisplaySourceLines (line, context_before, context_after, s);
85
86 return 0;
87}
88
89SourceManager::FileSP
Jim Inghamfdf24ef2011-09-08 22:13:49 +000090SourceManager::GetFile (const FileSpec &file_spec)
Chris Lattner24943d22010-06-08 16:52:24 +000091{
92 FileSP file_sp;
Jim Inghamcc637462011-09-13 00:29:56 +000093 file_sp = m_debugger->GetSourceFileCache().FindSourceFile (file_spec);
Johnny Chen931449e2011-12-12 21:59:28 +000094 // If file_sp is no good or it points to a non-existent file, reset it.
95 if (!file_sp || !file_sp->GetFileSpec().Exists())
Chris Lattner24943d22010-06-08 16:52:24 +000096 {
Jim Inghamfdf24ef2011-09-08 22:13:49 +000097 file_sp.reset (new File (file_spec, m_target));
Jim Inghamcc637462011-09-13 00:29:56 +000098
99 m_debugger->GetSourceFileCache().AddSourceFile(file_sp);
Chris Lattner24943d22010-06-08 16:52:24 +0000100 }
101 return file_sp;
102}
103
104size_t
105SourceManager::DisplaySourceLinesWithLineNumbersUsingLastFile
106(
107 uint32_t line,
108 uint32_t context_before,
109 uint32_t context_after,
110 const char* current_line_cstr,
Greg Clayton52c8b6e2011-04-19 04:19:37 +0000111 Stream *s,
112 const SymbolContextList *bp_locs
Chris Lattner24943d22010-06-08 16:52:24 +0000113)
114{
Jim Inghamcc637462011-09-13 00:29:56 +0000115 size_t return_value = 0;
Chris Lattner24943d22010-06-08 16:52:24 +0000116 if (line == 0)
117 {
118 if (m_last_file_line != 0
119 && m_last_file_line != UINT32_MAX)
120 line = m_last_file_line + context_before;
121 else
122 line = 1;
123 }
124
125 m_last_file_line = line + context_after + 1;
126 m_last_file_context_before = context_before;
127 m_last_file_context_after = context_after;
128
129 if (context_before == UINT32_MAX)
130 context_before = 0;
131 if (context_after == UINT32_MAX)
132 context_after = 10;
133
134 if (m_last_file_sp.get())
135 {
136 const uint32_t start_line = line <= context_before ? 1 : line - context_before;
137 const uint32_t end_line = line + context_after;
138 uint32_t curr_line;
139 for (curr_line = start_line; curr_line <= end_line; ++curr_line)
140 {
141 if (!m_last_file_sp->LineIsValid (curr_line))
142 {
143 m_last_file_line = UINT32_MAX;
144 break;
145 }
146
Greg Clayton52c8b6e2011-04-19 04:19:37 +0000147 char prefix[32] = "";
148 if (bp_locs)
149 {
150 uint32_t bp_count = bp_locs->NumLineEntriesWithLine (curr_line);
151
152 if (bp_count > 0)
153 ::snprintf (prefix, sizeof (prefix), "[%u] ", bp_count);
154 else
155 ::snprintf (prefix, sizeof (prefix), " ");
156 }
157
Jim Inghamcc637462011-09-13 00:29:56 +0000158 return_value += s->Printf("%s%2.2s %-4u\t",
Greg Clayton52c8b6e2011-04-19 04:19:37 +0000159 prefix,
160 curr_line == line ? current_line_cstr : "",
161 curr_line);
Jim Inghamcc637462011-09-13 00:29:56 +0000162 size_t this_line_size = m_last_file_sp->DisplaySourceLines (curr_line, 0, 0, s);
163 if (this_line_size == 0)
Chris Lattner24943d22010-06-08 16:52:24 +0000164 {
165 m_last_file_line = UINT32_MAX;
166 break;
167 }
Jim Inghamcc637462011-09-13 00:29:56 +0000168 else
169 return_value += this_line_size;
Chris Lattner24943d22010-06-08 16:52:24 +0000170 }
171 }
Jim Inghamcc637462011-09-13 00:29:56 +0000172 return return_value;
Chris Lattner24943d22010-06-08 16:52:24 +0000173}
174
175size_t
176SourceManager::DisplaySourceLinesWithLineNumbers
177(
178 const FileSpec &file_spec,
179 uint32_t line,
180 uint32_t context_before,
181 uint32_t context_after,
182 const char* current_line_cstr,
Greg Clayton52c8b6e2011-04-19 04:19:37 +0000183 Stream *s,
184 const SymbolContextList *bp_locs
Chris Lattner24943d22010-06-08 16:52:24 +0000185)
186{
187 bool same_as_previous = m_last_file_sp && m_last_file_sp->FileSpecMatches (file_spec);
188
189 if (!same_as_previous)
Jim Inghamfdf24ef2011-09-08 22:13:49 +0000190 m_last_file_sp = GetFile (file_spec);
Chris Lattner24943d22010-06-08 16:52:24 +0000191
192 if (line == 0)
193 {
194 if (!same_as_previous)
195 m_last_file_line = 0;
196 }
197
Greg Clayton52c8b6e2011-04-19 04:19:37 +0000198 return DisplaySourceLinesWithLineNumbersUsingLastFile (line, context_before, context_after, current_line_cstr, s, bp_locs);
Chris Lattner24943d22010-06-08 16:52:24 +0000199}
200
201size_t
Greg Clayton52c8b6e2011-04-19 04:19:37 +0000202SourceManager::DisplayMoreWithLineNumbers (Stream *s, const SymbolContextList *bp_locs)
Chris Lattner24943d22010-06-08 16:52:24 +0000203{
204 if (m_last_file_sp)
205 {
206 if (m_last_file_line == UINT32_MAX)
207 return 0;
Jim Inghamcc637462011-09-13 00:29:56 +0000208 return DisplaySourceLinesWithLineNumbersUsingLastFile (0, m_last_file_context_before, m_last_file_context_after, "", s, bp_locs);
Chris Lattner24943d22010-06-08 16:52:24 +0000209 }
210 return 0;
211}
212
Jim Inghamfdf24ef2011-09-08 22:13:49 +0000213bool
214SourceManager::SetDefaultFileAndLine (const FileSpec &file_spec, uint32_t line)
215{
216 FileSP old_file_sp = m_last_file_sp;
217 m_last_file_sp = GetFile (file_spec);
Jim Inghamca804bf2011-10-07 22:16:04 +0000218
219 m_default_set = true;
Jim Inghamfdf24ef2011-09-08 22:13:49 +0000220 if (m_last_file_sp)
221 {
222 m_last_file_line = line;
223 return true;
224 }
225 else
226 {
227 m_last_file_sp = old_file_sp;
228 return false;
229 }
230}
231
232bool
233SourceManager::GetDefaultFileAndLine (FileSpec &file_spec, uint32_t &line)
234{
235 if (m_last_file_sp)
236 {
237 file_spec = m_last_file_sp->GetFileSpec();
238 line = m_last_file_line;
239 return true;
240 }
Jim Inghamca804bf2011-10-07 22:16:04 +0000241 else if (!m_default_set)
242 {
243 // If nobody has set the default file and line then try here. If there's no executable, then we
244 // will try again later when there is one. Otherwise, if we can't find it we won't look again,
245 // somebody will have to set it (for instance when we stop somewhere...)
246 Module *executable_ptr = m_target->GetExecutableModulePointer();
247 if (executable_ptr)
248 {
249 SymbolContextList sc_list;
250 uint32_t num_matches;
251 ConstString main_name("main");
252 bool symbols_okay = false; // Force it to be a debug symbol.
Sean Callanan302d78c2012-02-10 22:52:19 +0000253 bool inlines_okay = true;
Jim Inghamca804bf2011-10-07 22:16:04 +0000254 bool append = false;
Sean Callanan302d78c2012-02-10 22:52:19 +0000255 num_matches = executable_ptr->FindFunctions (main_name, NULL, lldb::eFunctionNameTypeBase, inlines_okay, symbols_okay, append, sc_list);
Jim Inghamca804bf2011-10-07 22:16:04 +0000256 for (uint32_t idx = 0; idx < num_matches; idx++)
257 {
258 SymbolContext sc;
259 sc_list.GetContextAtIndex(idx, sc);
Greg Claytonc5d14e62011-12-10 21:05:26 +0000260 if (sc.function)
Jim Inghamca804bf2011-10-07 22:16:04 +0000261 {
Greg Claytonc5d14e62011-12-10 21:05:26 +0000262 lldb_private::LineEntry line_entry;
263 if (sc.function->GetAddressRange().GetBaseAddress().CalculateSymbolContextLineEntry (line_entry))
264 {
265 SetDefaultFileAndLine (line_entry.file,
266 line_entry.line);
267 file_spec = m_last_file_sp->GetFileSpec();
268 line = m_last_file_line;
269 return true;
270 }
Jim Inghamca804bf2011-10-07 22:16:04 +0000271 }
272 }
Jim Inghamca804bf2011-10-07 22:16:04 +0000273 }
Jim Inghamca804bf2011-10-07 22:16:04 +0000274 }
Greg Claytonc5d14e62011-12-10 21:05:26 +0000275 return false;
Jim Inghamfdf24ef2011-09-08 22:13:49 +0000276}
Chris Lattner24943d22010-06-08 16:52:24 +0000277
Jim Ingham03c8ee52011-09-21 01:17:13 +0000278void
279SourceManager::FindLinesMatchingRegex (FileSpec &file_spec,
Greg Clayton964deba2012-03-15 21:01:31 +0000280 RegularExpression& regex,
281 uint32_t start_line,
282 uint32_t end_line,
283 std::vector<uint32_t> &match_lines)
Jim Ingham03c8ee52011-09-21 01:17:13 +0000284{
285 match_lines.clear();
286 FileSP file_sp = GetFile (file_spec);
287 if (!file_sp)
288 return;
289 return file_sp->FindLinesMatchingRegex (regex, start_line, end_line, match_lines);
290}
Chris Lattner24943d22010-06-08 16:52:24 +0000291
Greg Claytonff44ab42011-04-23 02:04:55 +0000292SourceManager::File::File(const FileSpec &file_spec, Target *target) :
293 m_file_spec_orig (file_spec),
Chris Lattner24943d22010-06-08 16:52:24 +0000294 m_file_spec(file_spec),
Greg Claytonff44ab42011-04-23 02:04:55 +0000295 m_mod_time (file_spec.GetModificationTime()),
296 m_data_sp(),
Chris Lattner24943d22010-06-08 16:52:24 +0000297 m_offsets()
298{
Greg Claytonff44ab42011-04-23 02:04:55 +0000299 if (!m_mod_time.IsValid())
300 {
Jim Inghamcc637462011-09-13 00:29:56 +0000301 if (target)
302 {
303 if (!file_spec.GetDirectory() && file_spec.GetFilename())
304 {
305 // If this is just a file name, lets see if we can find it in the target:
306 bool check_inlines = false;
307 SymbolContextList sc_list;
308 size_t num_matches = target->GetImages().ResolveSymbolContextForFilePath (file_spec.GetFilename().AsCString(),
309 0,
310 check_inlines,
311 lldb::eSymbolContextModule | lldb::eSymbolContextCompUnit,
312 sc_list);
313 bool got_multiple = false;
314 if (num_matches != 0)
315 {
316 if (num_matches > 1)
317 {
318 SymbolContext sc;
319 FileSpec *test_cu_spec = NULL;
320
321 for (unsigned i = 0; i < num_matches; i++)
322 {
323 sc_list.GetContextAtIndex(i, sc);
324 if (sc.comp_unit)
325 {
326 if (test_cu_spec)
327 {
328 if (test_cu_spec != static_cast<FileSpec *> (sc.comp_unit))
329 got_multiple = true;
330 break;
331 }
332 else
333 test_cu_spec = sc.comp_unit;
334 }
335 }
336 }
337 if (!got_multiple)
338 {
339 SymbolContext sc;
340 sc_list.GetContextAtIndex (0, sc);
Greg Clayton964deba2012-03-15 21:01:31 +0000341 m_file_spec = sc.comp_unit;
Jim Inghamcc637462011-09-13 00:29:56 +0000342 m_mod_time = m_file_spec.GetModificationTime();
343 }
344 }
345 }
Johnny Chen931449e2011-12-12 21:59:28 +0000346 // Try remapping if m_file_spec does not correspond to an existing file.
347 if (!m_file_spec.Exists())
Jim Inghamcc637462011-09-13 00:29:56 +0000348 {
Greg Clayton964deba2012-03-15 21:01:31 +0000349 FileSpec new_file_spec;
350 // Check target specific source remappings first, then fall back to
351 // modules objects can have individual path remappings that were detected
352 // when the debug info for a module was found.
353 // then
354 if (target->GetSourcePathMap().FindFile (m_file_spec, new_file_spec) ||
355 target->GetImages().FindSourceFile (m_file_spec, new_file_spec))
Johnny Chen931449e2011-12-12 21:59:28 +0000356 {
Greg Clayton964deba2012-03-15 21:01:31 +0000357 m_file_spec = new_file_spec;
Johnny Chen931449e2011-12-12 21:59:28 +0000358 m_mod_time = m_file_spec.GetModificationTime();
359 }
Jim Inghamcc637462011-09-13 00:29:56 +0000360 }
361 }
Greg Claytonff44ab42011-04-23 02:04:55 +0000362 }
363
364 if (m_mod_time.IsValid())
365 m_data_sp = m_file_spec.ReadFileContents ();
Chris Lattner24943d22010-06-08 16:52:24 +0000366}
367
368SourceManager::File::~File()
369{
370}
371
372uint32_t
373SourceManager::File::GetLineOffset (uint32_t line)
374{
375 if (line == 0)
376 return UINT32_MAX;
377
378 if (line == 1)
379 return 0;
380
381 if (CalculateLineOffsets (line))
382 {
383 if (line < m_offsets.size())
384 return m_offsets[line - 1]; // yes we want "line - 1" in the index
385 }
386 return UINT32_MAX;
387}
388
389bool
390SourceManager::File::LineIsValid (uint32_t line)
391{
392 if (line == 0)
393 return false;
394
395 if (CalculateLineOffsets (line))
396 return line < m_offsets.size();
397 return false;
398}
399
400size_t
401SourceManager::File::DisplaySourceLines (uint32_t line, uint32_t context_before, uint32_t context_after, Stream *s)
402{
Greg Clayton61e2c302010-12-08 20:16:12 +0000403 // TODO: use host API to sign up for file modifications to anything in our
404 // source cache and only update when we determine a file has been updated.
405 // For now we check each time we want to display info for the file.
406 TimeValue curr_mod_time (m_file_spec.GetModificationTime());
Johnny Chen931449e2011-12-12 21:59:28 +0000407
408 if (curr_mod_time.IsValid() && m_mod_time != curr_mod_time)
Greg Clayton61e2c302010-12-08 20:16:12 +0000409 {
410 m_mod_time = curr_mod_time;
411 m_data_sp = m_file_spec.ReadFileContents ();
412 m_offsets.clear();
413 }
414
Johnny Chen931449e2011-12-12 21:59:28 +0000415 // Sanity check m_data_sp before proceeding.
416 if (!m_data_sp)
417 return 0;
418
Chris Lattner24943d22010-06-08 16:52:24 +0000419 const uint32_t start_line = line <= context_before ? 1 : line - context_before;
420 const uint32_t start_line_offset = GetLineOffset (start_line);
421 if (start_line_offset != UINT32_MAX)
422 {
423 const uint32_t end_line = line + context_after;
424 uint32_t end_line_offset = GetLineOffset (end_line + 1);
425 if (end_line_offset == UINT32_MAX)
426 end_line_offset = m_data_sp->GetByteSize();
427
428 assert (start_line_offset <= end_line_offset);
429 size_t bytes_written = 0;
430 if (start_line_offset < end_line_offset)
431 {
432 size_t count = end_line_offset - start_line_offset;
433 const uint8_t *cstr = m_data_sp->GetBytes() + start_line_offset;
434 bytes_written = s->Write(cstr, count);
435 if (!is_newline_char(cstr[count-1]))
436 bytes_written += s->EOL();
437 }
438 return bytes_written;
439 }
440 return 0;
441}
442
Jim Ingham03c8ee52011-09-21 01:17:13 +0000443void
444SourceManager::File::FindLinesMatchingRegex (RegularExpression& regex, uint32_t start_line, uint32_t end_line, std::vector<uint32_t> &match_lines)
445{
446 TimeValue curr_mod_time (m_file_spec.GetModificationTime());
447 if (m_mod_time != curr_mod_time)
448 {
449 m_mod_time = curr_mod_time;
450 m_data_sp = m_file_spec.ReadFileContents ();
451 m_offsets.clear();
452 }
453
454 match_lines.clear();
455
456 if (!LineIsValid(start_line) || (end_line != UINT32_MAX && !LineIsValid(end_line)))
457 return;
458 if (start_line > end_line)
459 return;
460
461 for (uint32_t line_no = start_line; line_no < end_line; line_no++)
462 {
463 std::string buffer;
464 if (!GetLine (line_no, buffer))
465 break;
466 if (regex.Execute(buffer.c_str()))
467 {
468 match_lines.push_back(line_no);
469 }
470 }
471}
472
Chris Lattner24943d22010-06-08 16:52:24 +0000473bool
474SourceManager::File::FileSpecMatches (const FileSpec &file_spec)
475{
Greg Clayton801417e2011-07-07 01:59:51 +0000476 return FileSpec::Equal (m_file_spec, file_spec, false);
Chris Lattner24943d22010-06-08 16:52:24 +0000477}
478
Jim Inghamcc637462011-09-13 00:29:56 +0000479bool
480lldb_private::operator== (const SourceManager::File &lhs, const SourceManager::File &rhs)
481{
482 if (lhs.m_file_spec == rhs.m_file_spec)
483 {
484 if (lhs.m_mod_time.IsValid())
485 {
486 if (rhs.m_mod_time.IsValid())
487 return lhs.m_mod_time == rhs.m_mod_time;
488 else
489 return false;
490 }
491 else if (rhs.m_mod_time.IsValid())
492 return false;
493 else
494 return true;
495 }
496 else
497 return false;
498}
Chris Lattner24943d22010-06-08 16:52:24 +0000499
500bool
501SourceManager::File::CalculateLineOffsets (uint32_t line)
502{
503 line = UINT32_MAX; // TODO: take this line out when we support partial indexing
504 if (line == UINT32_MAX)
505 {
506 // Already done?
507 if (!m_offsets.empty() && m_offsets[0] == UINT32_MAX)
508 return true;
509
510 if (m_offsets.empty())
511 {
512 if (m_data_sp.get() == NULL)
513 return false;
514
515 const char *start = (char *)m_data_sp->GetBytes();
516 if (start)
517 {
518 const char *end = start + m_data_sp->GetByteSize();
519
520 // Calculate all line offsets from scratch
521
522 // Push a 1 at index zero to indicate the file has been completely indexed.
523 m_offsets.push_back(UINT32_MAX);
524 register const char *s;
525 for (s = start; s < end; ++s)
526 {
527 register char curr_ch = *s;
528 if (is_newline_char (curr_ch))
529 {
530 register char next_ch = s[1];
531 if (is_newline_char (next_ch))
532 {
533 if (curr_ch != next_ch)
534 ++s;
535 }
536 m_offsets.push_back(s + 1 - start);
537 }
538 }
539 if (!m_offsets.empty())
540 {
541 if (m_offsets.back() < end - start)
542 m_offsets.push_back(end - start);
543 }
544 return true;
545 }
546 }
547 else
548 {
549 // Some lines have been populated, start where we last left off
550 assert(!"Not implemented yet");
551 }
552
553 }
554 else
555 {
556 // Calculate all line offsets up to "line"
557 assert(!"Not implemented yet");
558 }
559 return false;
560}
Jim Inghamcc637462011-09-13 00:29:56 +0000561
Jim Ingham03c8ee52011-09-21 01:17:13 +0000562bool
563SourceManager::File::GetLine (uint32_t line_no, std::string &buffer)
564{
565 if (!LineIsValid(line_no))
566 return false;
567
568 uint32_t start_offset = GetLineOffset (line_no);
569 uint32_t end_offset = GetLineOffset (line_no + 1);
570 if (end_offset == UINT32_MAX)
571 {
572 end_offset = m_data_sp->GetByteSize();
573 }
574 buffer.assign((char *) m_data_sp->GetBytes() + start_offset, end_offset - start_offset);
575
576 return true;
577}
578
Jim Inghamcc637462011-09-13 00:29:56 +0000579void
580SourceManager::SourceFileCache::AddSourceFile (const FileSP &file_sp)
581{
582 FileSpec file_spec;
583 FileCache::iterator pos = m_file_cache.find(file_spec);
584 if (pos == m_file_cache.end())
585 m_file_cache[file_spec] = file_sp;
586 else
587 {
588 if (file_sp != pos->second)
589 m_file_cache[file_spec] = file_sp;
590 }
591}
592
593SourceManager::FileSP
594SourceManager::SourceFileCache::FindSourceFile (const FileSpec &file_spec) const
595{
596 FileSP file_sp;
597 FileCache::const_iterator pos = m_file_cache.find(file_spec);
598 if (pos != m_file_cache.end())
599 file_sp = pos->second;
600 return file_sp;
601}
602