================ @@ -0,0 +1,469 @@ +//===--- APINotesManager.cpp - Manage API Notes Files ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/APINotes/APINotesManager.h" +#include "clang/APINotes/APINotesReader.h" +#include "clang/APINotes/APINotesYAMLCompiler.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/SourceMgrAdapter.h" +#include "clang/Basic/Version.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" + +using namespace clang; +using namespace api_notes; + +#define DEBUG_TYPE "API Notes" +STATISTIC(NumHeaderAPINotes, "non-framework API notes files loaded"); +STATISTIC(NumPublicFrameworkAPINotes, "framework public API notes loaded"); +STATISTIC(NumPrivateFrameworkAPINotes, "framework private API notes loaded"); +STATISTIC(NumFrameworksSearched, "frameworks searched"); +STATISTIC(NumDirectoriesSearched, "header directories searched"); +STATISTIC(NumDirectoryCacheHits, "directory cache hits"); + +namespace { +/// Prints two successive strings, which much be kept alive as long as the +/// PrettyStackTrace entry. +class PrettyStackTraceDoubleString : public llvm::PrettyStackTraceEntry { + StringRef First, Second; + +public: + PrettyStackTraceDoubleString(StringRef First, StringRef Second) + : First(First), Second(Second) {} + void print(raw_ostream &OS) const override { OS << First << Second; } +}; +} // namespace + +APINotesManager::APINotesManager(SourceManager &SM, const LangOptions &LangOpts) + : SM(SM), ImplicitAPINotes(LangOpts.APINotes) {} + +APINotesManager::~APINotesManager() { + // Free the API notes readers. + for (const auto &Entry : Readers) { + if (auto Reader = Entry.second.dyn_cast<APINotesReader *>()) + delete Reader; + } + + delete CurrentModuleReaders[0]; + delete CurrentModuleReaders[1]; +} + +std::unique_ptr<APINotesReader> +APINotesManager::loadAPINotes(FileEntryRef APINotesFile) { + PrettyStackTraceDoubleString Trace("Loading API notes from ", + APINotesFile.getName()); + + // Open the source file. + auto SourceFileID = SM.getOrCreateFileID(APINotesFile, SrcMgr::C_User); + auto SourceBuffer = SM.getBufferOrNone(SourceFileID, SourceLocation()); + if (!SourceBuffer) + return nullptr; + + // Compile the API notes source into a buffer. + // FIXME: Either propagate OSType through or, better yet, improve the binary + // APINotes format to maintain complete availability information. + // FIXME: We don't even really need to go through the binary format at all; + // we're just going to immediately deserialize it again. + llvm::SmallVector<char, 1024> APINotesBuffer; + std::unique_ptr<llvm::MemoryBuffer> CompiledBuffer; + { + SourceMgrAdapter SMAdapter( + SM, SM.getDiagnostics(), diag::err_apinotes_message, + diag::warn_apinotes_message, diag::note_apinotes_message, APINotesFile); + llvm::raw_svector_ostream OS(APINotesBuffer); + if (api_notes::compileAPINotes( + SourceBuffer->getBuffer(), SM.getFileEntryForID(SourceFileID), OS, + SMAdapter.getDiagHandler(), SMAdapter.getDiagContext())) + return nullptr; + + // Make a copy of the compiled form into the buffer. + CompiledBuffer = llvm::MemoryBuffer::getMemBufferCopy( + StringRef(APINotesBuffer.data(), APINotesBuffer.size())); + } + + // Load the binary form we just compiled. + auto Reader = APINotesReader::Create(std::move(CompiledBuffer), SwiftVersion); + assert(Reader && "Could not load the API notes we just generated?"); + return Reader; +} + +std::unique_ptr<APINotesReader> +APINotesManager::loadAPINotes(StringRef Buffer) { + llvm::SmallVector<char, 1024> APINotesBuffer; + std::unique_ptr<llvm::MemoryBuffer> CompiledBuffer; + SourceMgrAdapter SMAdapter( + SM, SM.getDiagnostics(), diag::err_apinotes_message, + diag::warn_apinotes_message, diag::note_apinotes_message, std::nullopt); + llvm::raw_svector_ostream OS(APINotesBuffer); + + if (api_notes::compileAPINotes(Buffer, nullptr, OS, + SMAdapter.getDiagHandler(), + SMAdapter.getDiagContext())) + return nullptr; + + CompiledBuffer = llvm::MemoryBuffer::getMemBufferCopy( + StringRef(APINotesBuffer.data(), APINotesBuffer.size())); + auto Reader = APINotesReader::Create(std::move(CompiledBuffer), SwiftVersion); + assert(Reader && "Could not load the API notes we just generated?"); + return Reader; +} + +bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir, + FileEntryRef APINotesFile) { + assert(Readers.find(HeaderDir) == Readers.end()); + if (auto Reader = loadAPINotes(APINotesFile)) { + Readers[HeaderDir] = Reader.release(); + return false; + } + + Readers[HeaderDir] = nullptr; + return true; +} + +OptionalFileEntryRef +APINotesManager::findAPINotesFile(DirectoryEntryRef Directory, + StringRef Basename, bool WantPublic) { + FileManager &FM = SM.getFileManager(); + + llvm::SmallString<128> Path(Directory.getName()); + + StringRef BasenameSuffix = ""; + if (!WantPublic) + BasenameSuffix = "_private"; + + // Look for the source API notes file. + llvm::sys::path::append(Path, llvm::Twine(Basename) + BasenameSuffix + "." + + SOURCE_APINOTES_EXTENSION); + return FM.getOptionalFileRef(Path, /*Open*/ true); +} + +OptionalDirectoryEntryRef APINotesManager::loadFrameworkAPINotes( + llvm::StringRef FrameworkPath, llvm::StringRef FrameworkName, bool Public) { + FileManager &FM = SM.getFileManager(); + + llvm::SmallString<128> Path(FrameworkPath); + unsigned FrameworkNameLength = Path.size(); + + // Form the path to the APINotes file. + llvm::sys::path::append(Path, "APINotes"); + if (Public) + llvm::sys::path::append( + Path, (llvm::Twine(FrameworkName) + "." + SOURCE_APINOTES_EXTENSION)); + else + llvm::sys::path::append(Path, (llvm::Twine(FrameworkName) + "_private." + + SOURCE_APINOTES_EXTENSION)); + + // Try to open the APINotes file. + auto APINotesFile = FM.getOptionalFileRef(Path); + if (!APINotesFile) + return std::nullopt; + + // Form the path to the corresponding header directory. + Path.resize(FrameworkNameLength); + if (Public) + llvm::sys::path::append(Path, "Headers"); + else + llvm::sys::path::append(Path, "PrivateHeaders"); + + // Try to access the header directory. + auto HeaderDir = FM.getOptionalDirectoryRef(Path); + if (!HeaderDir) + return std::nullopt; + + // Try to load the API notes. + if (loadAPINotes(*HeaderDir, *APINotesFile)) + return std::nullopt; + + // Success: return the header directory. + if (Public) + ++NumPublicFrameworkAPINotes; + else + ++NumPrivateFrameworkAPINotes; ---------------- egorzhdan wrote:
Yeah, I'm also not sure if it would be worth it https://github.com/llvm/llvm-project/pull/72389 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits