//===- ExtractAPI/API.cpp ---------------------------------------*- C++ -*-===// // // 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 // //===----------------------------------------------------------------------===// /// /// \file /// This file implements the APIRecord and derived record structs, /// and the APISet class. /// //===----------------------------------------------------------------------===// #include "clang/ExtractAPI/API.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/ErrorHandling.h" #include using namespace clang::extractapi; using namespace llvm; SymbolReference::SymbolReference(const APIRecord *R) : Name(R->Name), USR(R->USR), Record(R) {} APIRecord *APIRecord::castFromRecordContext(const RecordContext *Ctx) { switch (Ctx->getKind()) { #define RECORD_CONTEXT(CLASS, KIND) \ case KIND: \ return static_cast(const_cast(Ctx)); #include "clang/ExtractAPI/APIRecords.inc" default: return nullptr; // llvm_unreachable("RecordContext derived class isn't propertly // implemented"); } } RecordContext *APIRecord::castToRecordContext(const APIRecord *Record) { if (!Record) return nullptr; switch (Record->getKind()) { #define RECORD_CONTEXT(CLASS, KIND) \ case KIND: \ return static_cast(const_cast(Record)); #include "clang/ExtractAPI/APIRecords.inc" default: return nullptr; // llvm_unreachable("RecordContext derived class isn't propertly // implemented"); } } bool RecordContext::IsWellFormed() const { // Check that First and Last are both null or both non-null. return (First == nullptr) == (Last == nullptr); } void RecordContext::stealRecordChain(RecordContext &Other) { assert(IsWellFormed()); // Other's record chain is empty, nothing to do if (Other.First == nullptr && Other.Last == nullptr) return; // If we don't have an empty chain append Other's chain into ours. if (First) Last->NextInContext = Other.First; else First = Other.First; Last = Other.Last; for (auto *StolenRecord = Other.First; StolenRecord != nullptr; StolenRecord = StolenRecord->getNextInContext()) StolenRecord->Parent = SymbolReference(cast(this)); // Delete Other's chain to ensure we don't accidentally traverse it. Other.First = nullptr; Other.Last = nullptr; } void RecordContext::addToRecordChain(APIRecord *Record) const { assert(IsWellFormed()); if (!First) { First = Record; Last = Record; return; } Last->NextInContext = Record; Last = Record; } void RecordContext::removeFromRecordChain(APIRecord *Record) { APIRecord *Prev = nullptr; for (APIRecord *Curr = First; Curr != Record; Curr = Curr->NextInContext) Prev = Curr; if (Prev) Prev->NextInContext = Record->NextInContext; else First = Record->NextInContext; if (Last == Record) Last = Prev; Record->NextInContext = nullptr; } APIRecord *APISet::findRecordForUSR(StringRef USR) const { if (USR.empty()) return nullptr; auto FindIt = USRBasedLookupTable.find(USR); if (FindIt != USRBasedLookupTable.end()) return FindIt->getSecond().get(); return nullptr; } StringRef APISet::copyString(StringRef String) { if (String.empty()) return {}; // No need to allocate memory and copy if the string has already been stored. if (Allocator.identifyObject(String.data())) return String; void *Ptr = Allocator.Allocate(String.size(), 1); memcpy(Ptr, String.data(), String.size()); return StringRef(reinterpret_cast(Ptr), String.size()); } SymbolReference APISet::createSymbolReference(StringRef Name, StringRef USR, StringRef Source) { return SymbolReference(copyString(Name), copyString(USR), copyString(Source)); } void APISet::removeRecord(StringRef USR) { auto Result = USRBasedLookupTable.find(USR); if (Result != USRBasedLookupTable.end()) { auto *Record = Result->getSecond().get(); auto &ParentReference = Record->Parent; auto *ParentRecord = const_cast(ParentReference.Record); if (!ParentRecord) ParentRecord = findRecordForUSR(ParentReference.USR); if (auto *ParentCtx = llvm::cast_if_present(ParentRecord)) { ParentCtx->removeFromRecordChain(Record); if (auto *RecordAsCtx = llvm::dyn_cast(Record)) ParentCtx->stealRecordChain(*RecordAsCtx); } else { auto *It = llvm::find(TopLevelRecords, Record); if (It != TopLevelRecords.end()) TopLevelRecords.erase(It); if (auto *RecordAsCtx = llvm::dyn_cast(Record)) { for (const auto *Child = RecordAsCtx->First; Child != nullptr; Child = Child->getNextInContext()) TopLevelRecords.push_back(Child); } } USRBasedLookupTable.erase(Result); } } void APISet::removeRecord(APIRecord *Record) { removeRecord(Record->USR); } APIRecord::~APIRecord() {} TagRecord::~TagRecord() {} RecordRecord::~RecordRecord() {} RecordFieldRecord::~RecordFieldRecord() {} ObjCContainerRecord::~ObjCContainerRecord() {} ObjCMethodRecord::~ObjCMethodRecord() {} ObjCPropertyRecord::~ObjCPropertyRecord() {} CXXMethodRecord::~CXXMethodRecord() {} void GlobalFunctionRecord::anchor() {} void GlobalVariableRecord::anchor() {} void EnumConstantRecord::anchor() {} void EnumRecord::anchor() {} void StructFieldRecord::anchor() {} void StructRecord::anchor() {} void UnionFieldRecord::anchor() {} void UnionRecord::anchor() {} void CXXFieldRecord::anchor() {} void CXXClassRecord::anchor() {} void CXXConstructorRecord::anchor() {} void CXXDestructorRecord::anchor() {} void CXXInstanceMethodRecord::anchor() {} void CXXStaticMethodRecord::anchor() {} void ObjCInstancePropertyRecord::anchor() {} void ObjCClassPropertyRecord::anchor() {} void ObjCInstanceVariableRecord::anchor() {} void ObjCInstanceMethodRecord::anchor() {} void ObjCClassMethodRecord::anchor() {} void ObjCCategoryRecord::anchor() {} void ObjCInterfaceRecord::anchor() {} void ObjCProtocolRecord::anchor() {} void MacroDefinitionRecord::anchor() {} void TypedefRecord::anchor() {}