238 lines · cpp
1//===-- FileSpecList.cpp --------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Utility/FileSpecList.h"10#include "lldb/Target/Statistics.h"11#include "lldb/Target/Target.h"12#include "lldb/Utility/ConstString.h"13#include "lldb/Utility/LLDBLog.h"14#include "lldb/Utility/Log.h"15#include "lldb/Utility/RealpathPrefixes.h"16#include "lldb/Utility/Stream.h"17 18#include <cstdint>19#include <utility>20 21using namespace lldb_private;22 23FileSpecList::FileSpecList() : m_files() {}24 25FileSpecList::~FileSpecList() = default;26 27// Append the "file_spec" to the end of the file spec list.28void FileSpecList::Append(const FileSpec &file_spec) {29 m_files.push_back(file_spec);30}31 32// Only append the "file_spec" if this list doesn't already contain it.33//34// Returns true if "file_spec" was added, false if this list already contained35// a copy of "file_spec".36bool FileSpecList::AppendIfUnique(const FileSpec &file_spec) {37 collection::iterator end = m_files.end();38 if (find(m_files.begin(), end, file_spec) == end) {39 m_files.push_back(file_spec);40 return true;41 }42 return false;43}44 45// FIXME: Replace this with a DenseSet at the call site. It is inefficient.46bool SupportFileList::AppendIfUnique(const FileSpec &file_spec) {47 collection::iterator end = m_files.end();48 if (find_if(m_files.begin(), end, [&](const SupportFileNSP &support_file) {49 return support_file->GetSpecOnly() == file_spec;50 }) == end) {51 Append(file_spec);52 return true;53 }54 return false;55}56 57// Clears the file list.58void FileSpecList::Clear() { m_files.clear(); }59 60// Dumps the file list to the supplied stream pointer "s".61void FileSpecList::Dump(Stream *s, const char *separator_cstr) const {62 collection::const_iterator pos, end = m_files.end();63 for (pos = m_files.begin(); pos != end; ++pos) {64 pos->Dump(s->AsRawOstream());65 if (separator_cstr && ((pos + 1) != end))66 s->PutCString(separator_cstr);67 }68}69 70// Find the index of the file in the file spec list that matches "file_spec"71// starting "start_idx" entries into the file spec list.72//73// Returns the valid index of the file that matches "file_spec" if it is found,74// else std::numeric_limits<uint32_t>::max() is returned.75static size_t FindFileIndex(size_t start_idx, const FileSpec &file_spec,76 bool full, size_t num_files,77 std::function<const FileSpec &(size_t)> get_ith) {78 // When looking for files, we will compare only the filename if the FILE_SPEC79 // argument is empty80 bool compare_filename_only = file_spec.GetDirectory().IsEmpty();81 82 for (size_t idx = start_idx; idx < num_files; ++idx) {83 const FileSpec &ith = get_ith(idx);84 if (compare_filename_only) {85 if (ConstString::Equals(ith.GetFilename(), file_spec.GetFilename(),86 file_spec.IsCaseSensitive() ||87 ith.IsCaseSensitive()))88 return idx;89 } else {90 if (FileSpec::Equal(ith, file_spec, full))91 return idx;92 }93 }94 95 // We didn't find the file, return an invalid index96 return UINT32_MAX;97}98 99size_t FileSpecList::FindFileIndex(size_t start_idx, const FileSpec &file_spec,100 bool full) const {101 return ::FindFileIndex(102 start_idx, file_spec, full, m_files.size(),103 [&](size_t idx) -> const FileSpec & { return m_files[idx]; });104}105 106size_t SupportFileList::FindFileIndex(size_t start_idx,107 const FileSpec &file_spec,108 bool full) const {109 return ::FindFileIndex(start_idx, file_spec, full, m_files.size(),110 [&](size_t idx) -> const FileSpec & {111 return m_files[idx]->GetSpecOnly();112 });113}114 115enum IsCompatibleResult {116 kNoMatch = 0,117 kOnlyFileMatch = 1,118 kBothDirectoryAndFileMatch = 2,119};120 121IsCompatibleResult IsCompatible(const FileSpec &curr_file,122 const FileSpec &file_spec) {123 const bool file_spec_relative = file_spec.IsRelative();124 const bool file_spec_case_sensitive = file_spec.IsCaseSensitive();125 // When looking for files, we will compare only the filename if the directory126 // argument is empty in file_spec127 const bool full = !file_spec.GetDirectory().IsEmpty();128 129 // Always start by matching the filename first130 if (!curr_file.FileEquals(file_spec))131 return IsCompatibleResult::kNoMatch;132 133 // Only compare the full name if the we were asked to and if the current134 // file entry has a directory. If it doesn't have a directory then we only135 // compare the filename.136 if (FileSpec::Equal(curr_file, file_spec, full)) {137 return IsCompatibleResult::kBothDirectoryAndFileMatch;138 } else if (curr_file.IsRelative() || file_spec_relative) {139 llvm::StringRef curr_file_dir = curr_file.GetDirectory().GetStringRef();140 if (curr_file_dir.empty())141 // Basename match only for this file in the list142 return IsCompatibleResult::kBothDirectoryAndFileMatch;143 144 // Check if we have a relative path in our file list, or if "file_spec" is145 // relative, if so, check if either ends with the other.146 llvm::StringRef file_spec_dir = file_spec.GetDirectory().GetStringRef();147 // We have a relative path in our file list, it matches if the148 // specified path ends with this path, but we must ensure the full149 // component matches (we don't want "foo/bar.cpp" to match "oo/bar.cpp").150 auto is_suffix = [](llvm::StringRef a, llvm::StringRef b,151 bool case_sensitive) -> bool {152 if (case_sensitive ? a.consume_back(b) : a.consume_back_insensitive(b))153 return a.empty() || a.ends_with("/");154 return false;155 };156 const bool case_sensitive =157 file_spec_case_sensitive || curr_file.IsCaseSensitive();158 if (is_suffix(curr_file_dir, file_spec_dir, case_sensitive) ||159 is_suffix(file_spec_dir, curr_file_dir, case_sensitive))160 return IsCompatibleResult::kBothDirectoryAndFileMatch;161 }162 return IsCompatibleResult::kOnlyFileMatch;163}164 165size_t SupportFileList::FindCompatibleIndex(166 size_t start_idx, const FileSpec &file_spec,167 RealpathPrefixes *realpath_prefixes) const {168 const size_t num_files = m_files.size();169 if (start_idx >= num_files)170 return UINT32_MAX;171 172 for (size_t idx = start_idx; idx < num_files; ++idx) {173 const FileSpec &curr_file = m_files[idx]->GetSpecOnly();174 175 IsCompatibleResult result = IsCompatible(curr_file, file_spec);176 if (result == IsCompatibleResult::kBothDirectoryAndFileMatch)177 return idx;178 179 if (realpath_prefixes && result == IsCompatibleResult::kOnlyFileMatch) {180 if (std::optional<FileSpec> resolved_curr_file =181 realpath_prefixes->ResolveSymlinks(curr_file)) {182 if (IsCompatible(*resolved_curr_file, file_spec) ==183 IsCompatibleResult::kBothDirectoryAndFileMatch) {184 // Stats and logging.185 realpath_prefixes->IncreaseSourceRealpathCompatibleCount();186 Log *log = GetLog(LLDBLog::Source);187 LLDB_LOGF(log,188 "Realpath'ed support file %s is compatible to input file",189 resolved_curr_file->GetPath().c_str());190 // We found a match191 return idx;192 }193 }194 }195 }196 197 // We didn't find the file, return an invalid index198 return UINT32_MAX;199}200// Returns the FileSpec object at index "idx". If "idx" is out of range, then201// an empty FileSpec object will be returned.202const FileSpec &FileSpecList::GetFileSpecAtIndex(size_t idx) const {203 if (idx < m_files.size())204 return m_files[idx];205 static FileSpec g_empty_file_spec;206 return g_empty_file_spec;207}208 209const FileSpec &SupportFileList::GetFileSpecAtIndex(size_t idx) const {210 if (idx < m_files.size())211 return m_files[idx]->Materialize();212 static FileSpec g_empty_file_spec;213 return g_empty_file_spec;214}215 216SupportFileNSP SupportFileList::GetSupportFileAtIndex(size_t idx) const {217 if (idx < m_files.size())218 return m_files[idx];219 return std::make_shared<SupportFile>();220}221 222// Return the size in bytes that this object takes in memory. This returns the223// size in bytes of this object's member variables and any FileSpec objects its224// member variables contain, the result doesn't not include the string values225// for the directories any filenames as those are in shared string pools.226size_t FileSpecList::MemorySize() const {227 size_t mem_size = sizeof(FileSpecList);228 collection::const_iterator pos, end = m_files.end();229 for (pos = m_files.begin(); pos != end; ++pos) {230 mem_size += pos->MemorySize();231 }232 233 return mem_size;234}235 236// Return the number of files in the file spec list.237size_t FileSpecList::GetSize() const { return m_files.size(); }238