text: //===-- CompileUnit.cpp -----------------------------------------*- C++ -*-===//
  //
  //                     The LLVM Compiler Infrastructure
  //
  // This file is distributed under the University of Illinois Open Source
  // License. See LICENSE.TXT for details.
  //
  //===----------------------------------------------------------------------===//
  
  #include "lldb/Symbol/CompileUnit.h"
  #include "lldb/Core/Module.h"
  #include "lldb/Core/Language.h"
  #include "lldb/Symbol/LineTable.h"
  #include "lldb/Symbol/SymbolVendor.h"
  #include "lldb/Symbol/VariableList.h"
  
  using namespace lldb;
  using namespace lldb_private;
  
  CompileUnit::CompileUnit (const lldb::ModuleSP &module_sp, void *user_data, const char *pathname, const lldb::user_id_t cu_sym_id, lldb::LanguageType language) :
      ModuleChild(module_sp),
      FileSpec (pathname, false),
      UserID(cu_sym_id),
      m_user_data (user_data),
      m_language (language),
      m_flags (0),
      m_functions (),
      m_support_files (),
      m_line_table_ap (),
      m_variables()
  {
      if (language != eLanguageTypeUnknown)
          m_flags.Set(flagsParsedLanguage);
      assert(module_sp);
  }
  
  CompileUnit::CompileUnit (const lldb::ModuleSP &module_sp, void *user_data, const FileSpec &fspec, const lldb::user_id_t cu_sym_id, lldb::LanguageType language) :
      ModuleChild(module_sp),
      FileSpec (fspec),
      UserID(cu_sym_id),
      m_user_data (user_data),
      m_language (language),
      m_flags (0),
      m_functions (),
      m_support_files (),
      m_line_table_ap (),
      m_variables()
  {
      if (language != eLanguageTypeUnknown)
          m_flags.Set(flagsParsedLanguage);
      assert(module_sp);
  }
  
  CompileUnit::~CompileUnit ()
  {
  }
  
  void
  CompileUnit::CalculateSymbolContext(SymbolContext* sc)
  {
      sc->comp_unit = this;
      GetModule()->CalculateSymbolContext(sc);
  }
  
  ModuleSP
  CompileUnit::CalculateSymbolContextModule ()
  {
      return GetModule();
  }
  
  CompileUnit *
  CompileUnit::CalculateSymbolContextCompileUnit ()
  {
      return this;
  }
  
  void
  CompileUnit::DumpSymbolContext(Stream *s)
  {
      GetModule()->DumpSymbolContext(s);
      s->Printf(", CompileUnit{0x%8.8" PRIx64 "}", GetID());
  }
  
  
  void
  CompileUnit::GetDescription(Stream *s, lldb::DescriptionLevel level) const
  {
      Language language(m_language);
      *s << "id = " << (const UserID&)*this << ", file = \"" << (const FileSpec&)*this << "\", language = \"" << language << '"';
  }
  
  
  //----------------------------------------------------------------------
  // Dump the current contents of this object. No functions that cause on
  // demand parsing of functions, globals, statics are called, so this
  // is a good function to call to get an idea of the current contents of
  // the CompileUnit object.
  //----------------------------------------------------------------------
  void
  CompileUnit::Dump(Stream *s, bool show_context) const
  {
      s->Printf("%p: ", static_cast<const void*>(this));
      s->Indent();
      *s << "CompileUnit" << static_cast<const UserID&>(*this)
         << ", language = \"" << reinterpret_cast<const Language&>(*this)
         << "\", file = '" << static_cast<const FileSpec&>(*this) << "'\n";
  
  //  m_types.Dump(s);
  
      if (m_variables.get())
      {
          s->IndentMore();
          m_variables->Dump(s, show_context);
          s->IndentLess();
      }
  
      if (!m_functions.empty())
      {
          s->IndentMore();
          std::vector<FunctionSP>::const_iterator pos;
          std::vector<FunctionSP>::const_iterator end = m_functions.end();
          for (pos = m_functions.begin(); pos != end; ++pos)
          {
              (*pos)->Dump(s, show_context);
          }
  
          s->IndentLess();
          s->EOL();
      }
  }
  
  //----------------------------------------------------------------------
  // Add a function to this compile unit
  //----------------------------------------------------------------------
  void
  CompileUnit::AddFunction(FunctionSP& funcSP)
  {
      // TODO: order these by address
      m_functions.push_back(funcSP);
  }
  
  FunctionSP
  CompileUnit::GetFunctionAtIndex (size_t idx)
  {
      FunctionSP funcSP;
      if (idx < m_functions.size())
          funcSP = m_functions[idx];
      return funcSP;
  }
  
  //----------------------------------------------------------------------
  // Find functions using the Mangled::Tokens token list. This
  // function currently implements an interactive approach designed to find
  // all instances of certain functions. It isn't designed to the
  // quickest way to lookup functions as it will need to iterate through
  // all functions and see if they match, though it does provide a powerful
  // and context sensitive way to search for all functions with a certain
  // name, all functions in a namespace, or all functions of a template
  // type. See Mangled::Tokens::Parse() comments for more information.
  //
  // The function prototype will need to change to return a list of
  // results. It was originally used to help debug the Mangled class
  // and the Mangled::Tokens::MatchesQuery() function and it currently
  // will print out a list of matching results for the functions that
  // are currently in this compile unit.
  //
  // A FindFunctions method should be called prior to this that takes
  // a regular function name (const char * or ConstString as a parameter)
  // before resorting to this slower but more complete function. The
  // other FindFunctions method should be able to take advantage of any
  // accelerator tables available in the debug information (which is
  // parsed by the SymbolFile parser plug-ins and registered with each
  // Module).
  //----------------------------------------------------------------------
  //void
  //CompileUnit::FindFunctions(const Mangled::Tokens& tokens)
  //{
  //  if (!m_functions.empty())
  //  {
  //      Stream s(stdout);
  //      std::vector<FunctionSP>::const_iterator pos;
  //      std::vector<FunctionSP>::const_iterator end = m_functions.end();
  //      for (pos = m_functions.begin(); pos != end; ++pos)
  //      {
  //          const ConstString& demangled = (*pos)->Mangled().Demangled();
  //          if (demangled)
  //          {
  //              const Mangled::Tokens& func_tokens = (*pos)->Mangled().GetTokens();
  //              if (func_tokens.MatchesQuery (tokens))
  //                  s << "demangled MATCH found: " << demangled << "\n";
  //          }
  //      }
  //  }
  //}
  
  FunctionSP
  CompileUnit::FindFunctionByUID (lldb::user_id_t func_uid)
  {
      FunctionSP funcSP;
      if (!m_functions.empty())
      {
          std::vector<FunctionSP>::const_iterator pos;
          std::vector<FunctionSP>::const_iterator end = m_functions.end();
          for (pos = m_functions.begin(); pos != end; ++pos)
          {
              if ((*pos)->GetID() == func_uid)
              {
                  funcSP = *pos;
                  break;
              }
          }
      }
      return funcSP;
  }
  
  
  lldb::LanguageType
  CompileUnit::GetLanguage()
  {
      if (m_language == eLanguageTypeUnknown)
      {
          if (m_flags.IsClear(flagsParsedLanguage))
          {
              m_flags.Set(flagsParsedLanguage);
              SymbolVendor* symbol_vendor = GetModule()->GetSymbolVendor();
              if (symbol_vendor)
              {
                  SymbolContext sc;
                  CalculateSymbolContext(&sc);
                  m_language = symbol_vendor->ParseCompileUnitLanguage(sc);
              }
          }
      }
      return m_language;
  }
  
  LineTable*
  CompileUnit::GetLineTable()
  {
      if (m_line_table_ap.get() == nullptr)
      {
          if (m_flags.IsClear(flagsParsedLineTable))
          {
              m_flags.Set(flagsParsedLineTable);
              SymbolVendor* symbol_vendor = GetModule()->GetSymbolVendor();
              if (symbol_vendor)
              {
                  SymbolContext sc;
                  CalculateSymbolContext(&sc);
                  symbol_vendor->ParseCompileUnitLineTable(sc);
              }
          }
      }
      return m_line_table_ap.get();
  }
  
  void
  CompileUnit::SetLineTable(LineTable* line_table)
  {
      if (line_table == nullptr)
          m_flags.Clear(flagsParsedLineTable);
      else
          m_flags.Set(flagsParsedLineTable);
      m_line_table_ap.reset(line_table);
  }
  
  VariableListSP
  CompileUnit::GetVariableList(bool can_create)
  {
      if (m_variables.get() == nullptr && can_create)
      {
          SymbolContext sc;
          CalculateSymbolContext(&sc);
          assert(sc.module_sp);
          sc.module_sp->GetSymbolVendor()->ParseVariablesForContext(sc);
      }
  
      return m_variables;
  }
  
  uint32_t
  CompileUnit::FindLineEntry (uint32_t start_idx, uint32_t line, const FileSpec* file_spec_ptr, bool exact, LineEntry *line_entry_ptr)
  {
      uint32_t file_idx = 0;
  
      if (file_spec_ptr)
      {
          file_idx = GetSupportFiles().FindFileIndex (1, *file_spec_ptr, true);
          if (file_idx == UINT32_MAX)
              return UINT32_MAX;
      }
      else
      {
          // All the line table entries actually point to the version of the Compile
          // Unit that is in the support files (the one at 0 was artificially added.)
          // So prefer the one further on in the support files if it exists...
          FileSpecList &support_files = GetSupportFiles();
          const bool full = true;
          file_idx = support_files.FindFileIndex (1, support_files.GetFileSpecAtIndex(0), full);
          if (file_idx == UINT32_MAX)
              file_idx = 0;
      }
      LineTable *line_table = GetLineTable();
      if (line_table)
          return line_table->FindLineEntryIndexByFileIndex (start_idx, file_idx, line, exact, line_entry_ptr);
      return UINT32_MAX;
  }
  
  
  
  
  uint32_t
  CompileUnit::ResolveSymbolContext
  (
      const FileSpec& file_spec,
      uint32_t line,
      bool check_inlines,
      bool exact,
      uint32_t resolve_scope,
      SymbolContextList &sc_list
  )
  {
      // First find all of the file indexes that match our "file_spec". If 
      // "file_spec" has an empty directory, then only compare the basenames
      // when finding file indexes
      std::vector<uint32_t> file_indexes;
      const bool full_match = (bool)file_spec.GetDirectory();
      const bool remove_backup_dots = true;
      bool file_spec_matches_cu_file_spec = FileSpec::Equal(file_spec, *this, full_match, remove_backup_dots);
  
      // If we are not looking for inlined functions and our file spec doesn't
      // match then we are done...
      if (file_spec_matches_cu_file_spec == false && check_inlines == false)
          return 0;
  
      uint32_t file_idx = GetSupportFiles().FindFileIndex (1, file_spec, true, remove_backup_dots);
      while (file_idx != UINT32_MAX)
      {
          file_indexes.push_back (file_idx);
          file_idx = GetSupportFiles().FindFileIndex (file_idx + 1, file_spec, true, remove_backup_dots);
      }
      
      const size_t num_file_indexes = file_indexes.size();
      if (num_file_indexes == 0)
          return 0;
  
      const uint32_t prev_size = sc_list.GetSize();
  
      SymbolContext sc(GetModule());
      sc.comp_unit = this;
  
  
      if (line != 0)
      {
          LineTable *line_table = sc.comp_unit->GetLineTable();
  
          if (line_table != nullptr)
          {
              uint32_t found_line;
              uint32_t line_idx;
              
              if (num_file_indexes == 1)
              {
                  // We only have a single support file that matches, so use
                  // the line table function that searches for a line entries
                  // that match a single support file index
                  LineEntry line_entry;
                  line_idx = line_table->FindLineEntryIndexByFileIndex (0, file_indexes.front(), line, exact, &line_entry);
  
                  // If "exact == true", then "found_line" will be the same
                  // as "line". If "exact == false", the "found_line" will be the
                  // closest line entry with a line number greater than "line" and 
                  // we will use this for our subsequent line exact matches below.
                  found_line = line_entry.line;
  
                  while (line_idx != UINT32_MAX)
                  {
                      // If they only asked for the line entry, then we're done, we can just copy that over.
                      // But if they wanted more than just the line number, fill it in.
                      if (resolve_scope == eSymbolContextLineEntry)
                      {
                          sc.line_entry = line_entry;
                      }
                      else
                      {
                          line_entry.range.GetBaseAddress().CalculateSymbolContext(&sc, resolve_scope);
                      }
                      
                      sc_list.Append(sc);
                      line_idx = line_table->FindLineEntryIndexByFileIndex (line_idx + 1, file_indexes.front(), found_line, true, &line_entry);
                  }
              }
              else
              {
                  // We found multiple support files that match "file_spec" so use
                  // the line table function that searches for a line entries
                  // that match a multiple support file indexes.
                  LineEntry line_entry;
                  line_idx = line_table->FindLineEntryIndexByFileIndex (0, file_indexes, line, exact, &line_entry);
  
                  // If "exact == true", then "found_line" will be the same
                  // as "line". If "exact == false", the "found_line" will be the
                  // closest line entry with a line number greater than "line" and 
                  // we will use this for our subsequent line exact matches below.
                  found_line = line_entry.line;
  
                  while (line_idx != UINT32_MAX)
                  {
                      if (resolve_scope == eSymbolContextLineEntry)
                      {
                          sc.line_entry = line_entry;
                      }
                      else
                      {
                          line_entry.range.GetBaseAddress().CalculateSymbolContext(&sc, resolve_scope);
                      }
                      
                      sc_list.Append(sc);
                      line_idx = line_table->FindLineEntryIndexByFileIndex (line_idx + 1, file_indexes, found_line, true, &line_entry);
                  }
              }
          }
      }
      else if (file_spec_matches_cu_file_spec && !check_inlines)
      {
          // only append the context if we aren't looking for inline call sites
          // by file and line and if the file spec matches that of the compile unit
          sc_list.Append(sc);
      }
      return sc_list.GetSize() - prev_size;
  }
  
  void
  CompileUnit::SetVariableList(VariableListSP &variables)
  {
      m_variables = variables;
  }
  
  const std::vector<ConstString> &
  CompileUnit::GetImportedModules ()
  {
      if (m_imported_modules.empty() &&
          m_flags.IsClear(flagsParsedImportedModules))
      {
          m_flags.Set(flagsParsedImportedModules);
          if (SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor())
          {
              SymbolContext sc;
              CalculateSymbolContext(&sc);
              symbol_vendor->ParseImportedModules(sc, m_imported_modules);
          }
      }
      return m_imported_modules;
  }
  
  FileSpecList&
  CompileUnit::GetSupportFiles ()
  {
      if (m_support_files.GetSize() == 0)
      {
          if (m_flags.IsClear(flagsParsedSupportFiles))
          {
              m_flags.Set(flagsParsedSupportFiles);
              SymbolVendor* symbol_vendor = GetModule()->GetSymbolVendor();
              if (symbol_vendor)
              {
                  SymbolContext sc;
                  CalculateSymbolContext(&sc);
                  symbol_vendor->ParseCompileUnitSupportFiles(sc, m_support_files);
              }
          }
      }
      return m_support_files;
  }
  
  void *
  CompileUnit::GetUserData () const
  {
      return m_user_data;
  }
  
  
  