Daniel Grumberg 0ee06c31aa [clang][extract-api] Stop allocating APIRecords via BumpPtrAllocator
Using a BumpPtrAllocator introduced memory leaks for APIRecords as they
contain a std::vector. This meant that we needed to always keep a
reference to the records in APISet and arrange for their destructor to
get called appropriately. This was further complicated by the need for
records to own sub-records as these subrecords would still need to be
allocated via the BumpPtrAllocator and the owning record would now need
to arrange for the destructor of its subrecords to be called
appropriately.

Since APIRecords contain a std::vector so whenever elements get added to
that there is an associated heap allocation regardless. Since
performance isn't currently our main priority it makes sense to use
regular unique_ptr to keep track of APIRecords, this way we don't need
to arrange for destructors to get called.

The BumpPtrAllocator is still used for strings such as USRs so that we
can easily de-duplicate them as necessary.

Differential Revision: https://reviews.llvm.org/D122331
2022-03-24 17:44:00 +00:00

134 lines
5.5 KiB
C++

//===- 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 "clang/AST/CommentCommandTraits.h"
#include "clang/AST/CommentLexer.h"
#include "clang/AST/RawCommentList.h"
#include "clang/Index/USRGeneration.h"
#include <memory>
using namespace clang::extractapi;
using namespace llvm;
GlobalRecord *APISet::addGlobal(GVKind Kind, StringRef Name, StringRef USR,
PresumedLoc Loc,
const AvailabilityInfo &Availability,
LinkageInfo Linkage, const DocComment &Comment,
DeclarationFragments Fragments,
DeclarationFragments SubHeading,
FunctionSignature Signature) {
auto Result = Globals.insert({Name, nullptr});
if (Result.second) {
// Create the record if it does not already exist.
auto Record = std::make_unique<GlobalRecord>(
Kind, Name, USR, Loc, Availability, Linkage, Comment, Fragments,
SubHeading, Signature);
Result.first->second = std::move(Record);
}
return Result.first->second.get();
}
GlobalRecord *
APISet::addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc,
const AvailabilityInfo &Availability, LinkageInfo Linkage,
const DocComment &Comment, DeclarationFragments Fragments,
DeclarationFragments SubHeading) {
return addGlobal(GVKind::Variable, Name, USR, Loc, Availability, Linkage,
Comment, Fragments, SubHeading, {});
}
GlobalRecord *
APISet::addFunction(StringRef Name, StringRef USR, PresumedLoc Loc,
const AvailabilityInfo &Availability, LinkageInfo Linkage,
const DocComment &Comment, DeclarationFragments Fragments,
DeclarationFragments SubHeading,
FunctionSignature Signature) {
return addGlobal(GVKind::Function, Name, USR, Loc, Availability, Linkage,
Comment, Fragments, SubHeading, Signature);
}
EnumConstantRecord *APISet::addEnumConstant(
EnumRecord *Enum, StringRef Name, StringRef USR, PresumedLoc Loc,
const AvailabilityInfo &Availability, const DocComment &Comment,
DeclarationFragments Declaration, DeclarationFragments SubHeading) {
auto Record = std::make_unique<EnumConstantRecord>(
Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
return Enum->Constants.emplace_back(std::move(Record)).get();
}
EnumRecord *APISet::addEnum(StringRef Name, StringRef USR, PresumedLoc Loc,
const AvailabilityInfo &Availability,
const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading) {
auto Result = Enums.insert({Name, nullptr});
if (Result.second) {
// Create the record if it does not already exist.
auto Record = std::make_unique<EnumRecord>(
Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
Result.first->second = std::move(Record);
}
return Result.first->second.get();
}
StructFieldRecord *APISet::addStructField(StructRecord *Struct, StringRef Name,
StringRef USR, PresumedLoc Loc,
const AvailabilityInfo &Availability,
const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading) {
auto Record = std::make_unique<StructFieldRecord>(
Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
return Struct->Fields.emplace_back(std::move(Record)).get();
}
StructRecord *APISet::addStruct(StringRef Name, StringRef USR, PresumedLoc Loc,
const AvailabilityInfo &Availability,
const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading) {
auto Result = Structs.insert({Name, nullptr});
if (Result.second) {
// Create the record if it does not already exist.
auto Record = std::make_unique<StructRecord>(
Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
Result.first->second = std::move(Record);
}
return Result.first->second.get();
}
StringRef APISet::recordUSR(const Decl *D) {
SmallString<128> USR;
index::generateUSRForDecl(D, USR);
return copyString(USR);
}
StringRef APISet::copyString(StringRef String) {
if (String.empty())
return {};
// No need to allocate memory and copy if the string has already been stored.
if (StringAllocator.identifyObject(String.data()))
return String;
void *Ptr = StringAllocator.Allocate(String.size(), 1);
memcpy(Ptr, String.data(), String.size());
return StringRef(reinterpret_cast<const char *>(Ptr), String.size());
}
APIRecord::~APIRecord() {}
void GlobalRecord::anchor() {}