728 lines
24 KiB
C++
728 lines
24 KiB
C++
//===- Visitor.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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/InstallAPI/Visitor.h"
|
|
#include "clang/AST/Availability.h"
|
|
#include "clang/AST/ParentMapContext.h"
|
|
#include "clang/AST/VTableBuilder.h"
|
|
#include "clang/Basic/Linkage.h"
|
|
#include "clang/InstallAPI/DylibVerifier.h"
|
|
#include "clang/InstallAPI/FrontendRecords.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/IR/DataLayout.h"
|
|
#include "llvm/IR/Mangler.h"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::MachO;
|
|
|
|
namespace {
|
|
enum class CXXLinkage {
|
|
ExternalLinkage,
|
|
LinkOnceODRLinkage,
|
|
WeakODRLinkage,
|
|
PrivateLinkage,
|
|
};
|
|
}
|
|
|
|
namespace clang::installapi {
|
|
|
|
// Exported NamedDecl needs to have external linkage and
|
|
// default visibility from LinkageComputer.
|
|
static bool isExported(const NamedDecl *D) {
|
|
auto LV = D->getLinkageAndVisibility();
|
|
return isExternallyVisible(LV.getLinkage()) &&
|
|
(LV.getVisibility() == DefaultVisibility);
|
|
}
|
|
|
|
static bool isInlined(const FunctionDecl *D) {
|
|
bool HasInlineAttribute = false;
|
|
bool NoCXXAttr =
|
|
(!D->getASTContext().getLangOpts().CPlusPlus &&
|
|
!D->getASTContext().getTargetInfo().getCXXABI().isMicrosoft() &&
|
|
!D->hasAttr<DLLExportAttr>());
|
|
|
|
// Check all redeclarations to find an inline attribute or keyword.
|
|
for (const auto *RD : D->redecls()) {
|
|
if (!RD->isInlined())
|
|
continue;
|
|
HasInlineAttribute = true;
|
|
if (!(NoCXXAttr || RD->hasAttr<GNUInlineAttr>()))
|
|
continue;
|
|
if (RD->doesThisDeclarationHaveABody() &&
|
|
RD->isInlineDefinitionExternallyVisible())
|
|
return false;
|
|
}
|
|
|
|
if (!HasInlineAttribute)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static SymbolFlags getFlags(bool WeakDef, bool ThreadLocal = false) {
|
|
SymbolFlags Result = SymbolFlags::None;
|
|
if (WeakDef)
|
|
Result |= SymbolFlags::WeakDefined;
|
|
if (ThreadLocal)
|
|
Result |= SymbolFlags::ThreadLocalValue;
|
|
|
|
return Result;
|
|
}
|
|
|
|
void InstallAPIVisitor::HandleTranslationUnit(ASTContext &ASTCtx) {
|
|
if (ASTCtx.getDiagnostics().hasErrorOccurred())
|
|
return;
|
|
|
|
auto *D = ASTCtx.getTranslationUnitDecl();
|
|
TraverseDecl(D);
|
|
}
|
|
|
|
std::string InstallAPIVisitor::getMangledName(const NamedDecl *D) const {
|
|
SmallString<256> Name;
|
|
if (MC->shouldMangleDeclName(D)) {
|
|
raw_svector_ostream NStream(Name);
|
|
MC->mangleName(D, NStream);
|
|
} else
|
|
Name += D->getNameAsString();
|
|
|
|
return getBackendMangledName(Name);
|
|
}
|
|
|
|
std::string InstallAPIVisitor::getBackendMangledName(Twine Name) const {
|
|
SmallString<256> FinalName;
|
|
Mangler::getNameWithPrefix(FinalName, Name, DataLayout(Layout));
|
|
return std::string(FinalName);
|
|
}
|
|
|
|
std::optional<HeaderType>
|
|
InstallAPIVisitor::getAccessForDecl(const NamedDecl *D) const {
|
|
SourceLocation Loc = D->getLocation();
|
|
if (Loc.isInvalid())
|
|
return std::nullopt;
|
|
|
|
// If the loc refers to a macro expansion, InstallAPI needs to first get the
|
|
// file location of the expansion.
|
|
auto FileLoc = SrcMgr.getFileLoc(Loc);
|
|
FileID ID = SrcMgr.getFileID(FileLoc);
|
|
if (ID.isInvalid())
|
|
return std::nullopt;
|
|
|
|
const FileEntry *FE = SrcMgr.getFileEntryForID(ID);
|
|
if (!FE)
|
|
return std::nullopt;
|
|
|
|
auto Header = Ctx.findAndRecordFile(FE, PP);
|
|
if (!Header.has_value())
|
|
return std::nullopt;
|
|
|
|
HeaderType Access = Header.value();
|
|
assert(Access != HeaderType::Unknown && "unexpected access level for global");
|
|
return Access;
|
|
}
|
|
|
|
/// Check if the interface itself or any of its super classes have an
|
|
/// exception attribute. InstallAPI needs to export an additional symbol
|
|
/// ("OBJC_EHTYPE_$CLASS_NAME") if any of the classes have the exception
|
|
/// attribute.
|
|
static bool hasObjCExceptionAttribute(const ObjCInterfaceDecl *D) {
|
|
for (; D != nullptr; D = D->getSuperClass())
|
|
if (D->hasAttr<ObjCExceptionAttr>())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
void InstallAPIVisitor::recordObjCInstanceVariables(
|
|
const ASTContext &ASTCtx, ObjCContainerRecord *Record, StringRef SuperClass,
|
|
const llvm::iterator_range<
|
|
DeclContext::specific_decl_iterator<ObjCIvarDecl>>
|
|
Ivars) {
|
|
RecordLinkage Linkage = RecordLinkage::Exported;
|
|
const RecordLinkage ContainerLinkage = Record->getLinkage();
|
|
// If fragile, set to unknown.
|
|
if (ASTCtx.getLangOpts().ObjCRuntime.isFragile())
|
|
Linkage = RecordLinkage::Unknown;
|
|
// Linkage should be inherited from container.
|
|
else if (ContainerLinkage != RecordLinkage::Unknown)
|
|
Linkage = ContainerLinkage;
|
|
for (const auto *IV : Ivars) {
|
|
auto Access = getAccessForDecl(IV);
|
|
if (!Access)
|
|
continue;
|
|
StringRef Name = IV->getName();
|
|
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(IV);
|
|
auto AC = IV->getCanonicalAccessControl();
|
|
auto [ObjCIVR, FA] =
|
|
Ctx.Slice->addObjCIVar(Record, Name, Linkage, Avail, IV, *Access, AC);
|
|
Ctx.Verifier->verify(ObjCIVR, FA, SuperClass);
|
|
}
|
|
}
|
|
|
|
bool InstallAPIVisitor::VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) {
|
|
// Skip forward declaration for classes (@class)
|
|
if (!D->isThisDeclarationADefinition())
|
|
return true;
|
|
|
|
// Skip over declarations that access could not be collected for.
|
|
auto Access = getAccessForDecl(D);
|
|
if (!Access)
|
|
return true;
|
|
|
|
StringRef Name = D->getObjCRuntimeNameAsString();
|
|
const RecordLinkage Linkage =
|
|
isExported(D) ? RecordLinkage::Exported : RecordLinkage::Internal;
|
|
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
|
|
const bool IsEHType =
|
|
(!D->getASTContext().getLangOpts().ObjCRuntime.isFragile() &&
|
|
hasObjCExceptionAttribute(D));
|
|
|
|
auto [Class, FA] =
|
|
Ctx.Slice->addObjCInterface(Name, Linkage, Avail, D, *Access, IsEHType);
|
|
Ctx.Verifier->verify(Class, FA);
|
|
|
|
// Get base class.
|
|
StringRef SuperClassName;
|
|
if (const auto *SuperClass = D->getSuperClass())
|
|
SuperClassName = SuperClass->getObjCRuntimeNameAsString();
|
|
|
|
recordObjCInstanceVariables(D->getASTContext(), Class, Class->getName(),
|
|
D->ivars());
|
|
return true;
|
|
}
|
|
|
|
bool InstallAPIVisitor::VisitObjCCategoryDecl(const ObjCCategoryDecl *D) {
|
|
StringRef CategoryName = D->getName();
|
|
// Skip over declarations that access could not be collected for.
|
|
auto Access = getAccessForDecl(D);
|
|
if (!Access)
|
|
return true;
|
|
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
|
|
const ObjCInterfaceDecl *InterfaceD = D->getClassInterface();
|
|
const StringRef InterfaceName = InterfaceD->getName();
|
|
|
|
ObjCCategoryRecord *CategoryRecord =
|
|
Ctx.Slice->addObjCCategory(InterfaceName, CategoryName, Avail, D, *Access)
|
|
.first;
|
|
recordObjCInstanceVariables(D->getASTContext(), CategoryRecord, InterfaceName,
|
|
D->ivars());
|
|
return true;
|
|
}
|
|
|
|
bool InstallAPIVisitor::VisitVarDecl(const VarDecl *D) {
|
|
// Skip function parameters.
|
|
if (isa<ParmVarDecl>(D))
|
|
return true;
|
|
|
|
// Skip variables in records. They are handled separately for C++.
|
|
if (D->getDeclContext()->isRecord())
|
|
return true;
|
|
|
|
// Skip anything inside functions or methods.
|
|
if (!D->isDefinedOutsideFunctionOrMethod())
|
|
return true;
|
|
|
|
// If this is a template but not specialization or instantiation, skip.
|
|
if (D->getASTContext().getTemplateOrSpecializationInfo(D) &&
|
|
D->getTemplateSpecializationKind() == TSK_Undeclared)
|
|
return true;
|
|
|
|
// Skip over declarations that access could not collected for.
|
|
auto Access = getAccessForDecl(D);
|
|
if (!Access)
|
|
return true;
|
|
|
|
const RecordLinkage Linkage =
|
|
isExported(D) ? RecordLinkage::Exported : RecordLinkage::Internal;
|
|
const bool WeakDef = D->hasAttr<WeakAttr>();
|
|
const bool ThreadLocal = D->getTLSKind() != VarDecl::TLS_None;
|
|
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
|
|
auto [GR, FA] = Ctx.Slice->addGlobal(getMangledName(D), Linkage,
|
|
GlobalRecord::Kind::Variable, Avail, D,
|
|
*Access, getFlags(WeakDef, ThreadLocal));
|
|
Ctx.Verifier->verify(GR, FA);
|
|
return true;
|
|
}
|
|
|
|
bool InstallAPIVisitor::VisitFunctionDecl(const FunctionDecl *D) {
|
|
if (const CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(D)) {
|
|
// Skip member function in class templates.
|
|
if (M->getParent()->getDescribedClassTemplate() != nullptr)
|
|
return true;
|
|
|
|
// Skip methods in CXX RecordDecls.
|
|
for (const DynTypedNode &P : D->getASTContext().getParents(*M)) {
|
|
if (P.get<CXXRecordDecl>())
|
|
return true;
|
|
}
|
|
|
|
// Skip CXX ConstructorDecls and DestructorDecls.
|
|
if (isa<CXXConstructorDecl>(M) || isa<CXXDestructorDecl>(M))
|
|
return true;
|
|
}
|
|
|
|
// Skip templated functions.
|
|
switch (D->getTemplatedKind()) {
|
|
case FunctionDecl::TK_NonTemplate:
|
|
case FunctionDecl::TK_DependentNonTemplate:
|
|
break;
|
|
case FunctionDecl::TK_MemberSpecialization:
|
|
case FunctionDecl::TK_FunctionTemplateSpecialization:
|
|
if (auto *TempInfo = D->getTemplateSpecializationInfo()) {
|
|
if (!TempInfo->isExplicitInstantiationOrSpecialization())
|
|
return true;
|
|
}
|
|
break;
|
|
case FunctionDecl::TK_FunctionTemplate:
|
|
case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
|
|
return true;
|
|
}
|
|
|
|
auto Access = getAccessForDecl(D);
|
|
if (!Access)
|
|
return true;
|
|
auto Name = getMangledName(D);
|
|
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
|
|
const bool ExplicitInstantiation = D->getTemplateSpecializationKind() ==
|
|
TSK_ExplicitInstantiationDeclaration;
|
|
const bool WeakDef = ExplicitInstantiation || D->hasAttr<WeakAttr>();
|
|
const bool Inlined = isInlined(D);
|
|
const RecordLinkage Linkage = (Inlined || !isExported(D))
|
|
? RecordLinkage::Internal
|
|
: RecordLinkage::Exported;
|
|
auto [GR, FA] =
|
|
Ctx.Slice->addGlobal(Name, Linkage, GlobalRecord::Kind::Function, Avail,
|
|
D, *Access, getFlags(WeakDef), Inlined);
|
|
Ctx.Verifier->verify(GR, FA);
|
|
return true;
|
|
}
|
|
|
|
static bool hasVTable(const CXXRecordDecl *D) {
|
|
// Check if vtable symbols should be emitted, only dynamic classes need
|
|
// vtables.
|
|
if (!D->hasDefinition() || !D->isDynamicClass())
|
|
return false;
|
|
|
|
assert(D->isExternallyVisible() && "Should be externally visible");
|
|
assert(D->isCompleteDefinition() && "Only works on complete definitions");
|
|
|
|
const CXXMethodDecl *KeyFunctionD =
|
|
D->getASTContext().getCurrentKeyFunction(D);
|
|
// If this class has a key function, then there is a vtable, possibly internal
|
|
// though.
|
|
if (KeyFunctionD) {
|
|
switch (KeyFunctionD->getTemplateSpecializationKind()) {
|
|
case TSK_Undeclared:
|
|
case TSK_ExplicitSpecialization:
|
|
case TSK_ImplicitInstantiation:
|
|
case TSK_ExplicitInstantiationDefinition:
|
|
return true;
|
|
case TSK_ExplicitInstantiationDeclaration:
|
|
llvm_unreachable(
|
|
"Unexpected TemplateSpecializationKind for key function");
|
|
}
|
|
} else if (D->isAbstract()) {
|
|
// If the class is abstract and it doesn't have a key function, it is a
|
|
// 'pure' virtual class. It doesn't need a vtable.
|
|
return false;
|
|
}
|
|
|
|
switch (D->getTemplateSpecializationKind()) {
|
|
case TSK_Undeclared:
|
|
case TSK_ExplicitSpecialization:
|
|
case TSK_ImplicitInstantiation:
|
|
return false;
|
|
|
|
case TSK_ExplicitInstantiationDeclaration:
|
|
case TSK_ExplicitInstantiationDefinition:
|
|
return true;
|
|
}
|
|
|
|
llvm_unreachable("Invalid TemplateSpecializationKind!");
|
|
}
|
|
|
|
static CXXLinkage getVTableLinkage(const CXXRecordDecl *D) {
|
|
assert((D->hasDefinition() && D->isDynamicClass()) && "Record has no vtable");
|
|
assert(D->isExternallyVisible() && "Record should be externally visible");
|
|
if (D->getVisibility() == HiddenVisibility)
|
|
return CXXLinkage::PrivateLinkage;
|
|
|
|
const CXXMethodDecl *KeyFunctionD =
|
|
D->getASTContext().getCurrentKeyFunction(D);
|
|
if (KeyFunctionD) {
|
|
// If this class has a key function, use that to determine the
|
|
// linkage of the vtable.
|
|
switch (KeyFunctionD->getTemplateSpecializationKind()) {
|
|
case TSK_Undeclared:
|
|
case TSK_ExplicitSpecialization:
|
|
if (isInlined(KeyFunctionD))
|
|
return CXXLinkage::LinkOnceODRLinkage;
|
|
return CXXLinkage::ExternalLinkage;
|
|
case TSK_ImplicitInstantiation:
|
|
llvm_unreachable("No external vtable for implicit instantiations");
|
|
case TSK_ExplicitInstantiationDefinition:
|
|
return CXXLinkage::WeakODRLinkage;
|
|
case TSK_ExplicitInstantiationDeclaration:
|
|
llvm_unreachable(
|
|
"Unexpected TemplateSpecializationKind for key function");
|
|
}
|
|
}
|
|
|
|
switch (D->getTemplateSpecializationKind()) {
|
|
case TSK_Undeclared:
|
|
case TSK_ExplicitSpecialization:
|
|
case TSK_ImplicitInstantiation:
|
|
return CXXLinkage::LinkOnceODRLinkage;
|
|
case TSK_ExplicitInstantiationDeclaration:
|
|
case TSK_ExplicitInstantiationDefinition:
|
|
return CXXLinkage::WeakODRLinkage;
|
|
}
|
|
|
|
llvm_unreachable("Invalid TemplateSpecializationKind!");
|
|
}
|
|
|
|
static bool isRTTIWeakDef(const CXXRecordDecl *D) {
|
|
if (D->hasAttr<WeakAttr>())
|
|
return true;
|
|
|
|
if (D->isAbstract() && D->getASTContext().getCurrentKeyFunction(D) == nullptr)
|
|
return true;
|
|
|
|
if (D->isDynamicClass())
|
|
return getVTableLinkage(D) != CXXLinkage::ExternalLinkage;
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool hasRTTI(const CXXRecordDecl *D) {
|
|
if (!D->getASTContext().getLangOpts().RTTI)
|
|
return false;
|
|
|
|
if (!D->hasDefinition())
|
|
return false;
|
|
|
|
if (!D->isDynamicClass())
|
|
return false;
|
|
|
|
// Don't emit weak-def RTTI information. InstallAPI cannot reliably determine
|
|
// if the final binary will have those weak defined RTTI symbols. This depends
|
|
// on the optimization level and if the class has been instantiated and used.
|
|
//
|
|
// Luckily, the Apple static linker doesn't need those weak defined RTTI
|
|
// symbols for linking. They are only needed by the runtime linker. That means
|
|
// they can be safely dropped.
|
|
if (isRTTIWeakDef(D))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
std::string
|
|
InstallAPIVisitor::getMangledCXXRTTIName(const CXXRecordDecl *D) const {
|
|
SmallString<256> Name;
|
|
raw_svector_ostream NameStream(Name);
|
|
MC->mangleCXXRTTIName(QualType(D->getTypeForDecl(), 0), NameStream);
|
|
|
|
return getBackendMangledName(Name);
|
|
}
|
|
|
|
std::string InstallAPIVisitor::getMangledCXXRTTI(const CXXRecordDecl *D) const {
|
|
SmallString<256> Name;
|
|
raw_svector_ostream NameStream(Name);
|
|
MC->mangleCXXRTTI(QualType(D->getTypeForDecl(), 0), NameStream);
|
|
|
|
return getBackendMangledName(Name);
|
|
}
|
|
|
|
std::string
|
|
InstallAPIVisitor::getMangledCXXVTableName(const CXXRecordDecl *D) const {
|
|
SmallString<256> Name;
|
|
raw_svector_ostream NameStream(Name);
|
|
MC->mangleCXXVTable(D, NameStream);
|
|
|
|
return getBackendMangledName(Name);
|
|
}
|
|
|
|
std::string
|
|
InstallAPIVisitor::getMangledCXXThunk(const GlobalDecl &D,
|
|
const ThunkInfo &Thunk) const {
|
|
SmallString<256> Name;
|
|
raw_svector_ostream NameStream(Name);
|
|
const auto *Method = cast<CXXMethodDecl>(D.getDecl());
|
|
if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method))
|
|
MC->mangleCXXDtorThunk(Dtor, D.getDtorType(), Thunk.This, NameStream);
|
|
else
|
|
MC->mangleThunk(Method, Thunk, NameStream);
|
|
|
|
return getBackendMangledName(Name);
|
|
}
|
|
|
|
std::string InstallAPIVisitor::getMangledCtorDtor(const CXXMethodDecl *D,
|
|
int Type) const {
|
|
SmallString<256> Name;
|
|
raw_svector_ostream NameStream(Name);
|
|
GlobalDecl GD;
|
|
if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(D))
|
|
GD = GlobalDecl(Ctor, CXXCtorType(Type));
|
|
else {
|
|
const auto *Dtor = cast<CXXDestructorDecl>(D);
|
|
GD = GlobalDecl(Dtor, CXXDtorType(Type));
|
|
}
|
|
MC->mangleName(GD, NameStream);
|
|
return getBackendMangledName(Name);
|
|
}
|
|
|
|
void InstallAPIVisitor::emitVTableSymbols(const CXXRecordDecl *D,
|
|
const AvailabilityInfo &Avail,
|
|
const HeaderType Access,
|
|
bool EmittedVTable) {
|
|
if (hasVTable(D)) {
|
|
EmittedVTable = true;
|
|
const CXXLinkage VTableLinkage = getVTableLinkage(D);
|
|
if (VTableLinkage == CXXLinkage::ExternalLinkage ||
|
|
VTableLinkage == CXXLinkage::WeakODRLinkage) {
|
|
const std::string Name = getMangledCXXVTableName(D);
|
|
const bool WeakDef = VTableLinkage == CXXLinkage::WeakODRLinkage;
|
|
auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
|
|
GlobalRecord::Kind::Variable, Avail,
|
|
D, Access, getFlags(WeakDef));
|
|
Ctx.Verifier->verify(GR, FA);
|
|
if (!D->getDescribedClassTemplate() && !D->isInvalidDecl()) {
|
|
VTableContextBase *VTable = D->getASTContext().getVTableContext();
|
|
auto AddThunk = [&](GlobalDecl GD) {
|
|
const ItaniumVTableContext::ThunkInfoVectorTy *Thunks =
|
|
VTable->getThunkInfo(GD);
|
|
if (!Thunks)
|
|
return;
|
|
|
|
for (const auto &Thunk : *Thunks) {
|
|
const std::string Name = getMangledCXXThunk(GD, Thunk);
|
|
auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
|
|
GlobalRecord::Kind::Function,
|
|
Avail, GD.getDecl(), Access);
|
|
Ctx.Verifier->verify(GR, FA);
|
|
}
|
|
};
|
|
|
|
for (const auto *Method : D->methods()) {
|
|
if (isa<CXXConstructorDecl>(Method) || !Method->isVirtual())
|
|
continue;
|
|
|
|
if (auto Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
|
|
// Skip default destructor.
|
|
if (Dtor->isDefaulted())
|
|
continue;
|
|
AddThunk({Dtor, Dtor_Deleting});
|
|
AddThunk({Dtor, Dtor_Complete});
|
|
} else
|
|
AddThunk(Method);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!EmittedVTable)
|
|
return;
|
|
|
|
if (hasRTTI(D)) {
|
|
std::string Name = getMangledCXXRTTI(D);
|
|
auto [GR, FA] =
|
|
Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
|
|
GlobalRecord::Kind::Variable, Avail, D, Access);
|
|
Ctx.Verifier->verify(GR, FA);
|
|
|
|
Name = getMangledCXXRTTIName(D);
|
|
auto [NamedGR, NamedFA] =
|
|
Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
|
|
GlobalRecord::Kind::Variable, Avail, D, Access);
|
|
Ctx.Verifier->verify(NamedGR, NamedFA);
|
|
}
|
|
|
|
for (const auto &It : D->bases()) {
|
|
const CXXRecordDecl *Base =
|
|
cast<CXXRecordDecl>(It.getType()->castAs<RecordType>()->getDecl());
|
|
const auto BaseAccess = getAccessForDecl(Base);
|
|
if (!BaseAccess)
|
|
continue;
|
|
const AvailabilityInfo BaseAvail = AvailabilityInfo::createFromDecl(Base);
|
|
emitVTableSymbols(Base, BaseAvail, *BaseAccess, /*EmittedVTable=*/true);
|
|
}
|
|
}
|
|
|
|
bool InstallAPIVisitor::VisitCXXRecordDecl(const CXXRecordDecl *D) {
|
|
if (!D->isCompleteDefinition())
|
|
return true;
|
|
|
|
// Skip templated classes.
|
|
if (D->getDescribedClassTemplate() != nullptr)
|
|
return true;
|
|
|
|
// Skip partial templated classes too.
|
|
if (isa<ClassTemplatePartialSpecializationDecl>(D))
|
|
return true;
|
|
|
|
auto Access = getAccessForDecl(D);
|
|
if (!Access)
|
|
return true;
|
|
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
|
|
|
|
// Check whether to emit the vtable/rtti symbols.
|
|
if (isExported(D))
|
|
emitVTableSymbols(D, Avail, *Access);
|
|
|
|
TemplateSpecializationKind ClassSK = TSK_Undeclared;
|
|
bool KeepInlineAsWeak = false;
|
|
if (auto *Templ = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
|
|
ClassSK = Templ->getTemplateSpecializationKind();
|
|
if (ClassSK == TSK_ExplicitInstantiationDeclaration)
|
|
KeepInlineAsWeak = true;
|
|
}
|
|
|
|
// Record the class methods.
|
|
for (const auto *M : D->methods()) {
|
|
// Inlined methods are usually not emitted, except when it comes from a
|
|
// specialized template.
|
|
bool WeakDef = false;
|
|
if (isInlined(M)) {
|
|
if (!KeepInlineAsWeak)
|
|
continue;
|
|
|
|
WeakDef = true;
|
|
}
|
|
|
|
if (!isExported(M))
|
|
continue;
|
|
|
|
switch (M->getTemplateSpecializationKind()) {
|
|
case TSK_Undeclared:
|
|
case TSK_ExplicitSpecialization:
|
|
break;
|
|
case TSK_ImplicitInstantiation:
|
|
continue;
|
|
case TSK_ExplicitInstantiationDeclaration:
|
|
if (ClassSK == TSK_ExplicitInstantiationDeclaration)
|
|
WeakDef = true;
|
|
break;
|
|
case TSK_ExplicitInstantiationDefinition:
|
|
WeakDef = true;
|
|
break;
|
|
}
|
|
|
|
if (!M->isUserProvided())
|
|
continue;
|
|
|
|
// Methods that are deleted are not exported.
|
|
if (M->isDeleted())
|
|
continue;
|
|
|
|
const auto Access = getAccessForDecl(M);
|
|
if (!Access)
|
|
return true;
|
|
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(M);
|
|
|
|
if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(M)) {
|
|
// Defaulted constructors are not exported.
|
|
if (Ctor->isDefaulted())
|
|
continue;
|
|
|
|
std::string Name = getMangledCtorDtor(M, Ctor_Base);
|
|
auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
|
|
GlobalRecord::Kind::Function, Avail,
|
|
D, *Access, getFlags(WeakDef));
|
|
Ctx.Verifier->verify(GR, FA);
|
|
|
|
if (!D->isAbstract()) {
|
|
std::string Name = getMangledCtorDtor(M, Ctor_Complete);
|
|
auto [GR, FA] = Ctx.Slice->addGlobal(
|
|
Name, RecordLinkage::Exported, GlobalRecord::Kind::Function, Avail,
|
|
D, *Access, getFlags(WeakDef));
|
|
Ctx.Verifier->verify(GR, FA);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(M)) {
|
|
// Defaulted destructors are not exported.
|
|
if (Dtor->isDefaulted())
|
|
continue;
|
|
|
|
std::string Name = getMangledCtorDtor(M, Dtor_Base);
|
|
auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
|
|
GlobalRecord::Kind::Function, Avail,
|
|
D, *Access, getFlags(WeakDef));
|
|
Ctx.Verifier->verify(GR, FA);
|
|
|
|
Name = getMangledCtorDtor(M, Dtor_Complete);
|
|
auto [CompleteGR, CompleteFA] = Ctx.Slice->addGlobal(
|
|
Name, RecordLinkage::Exported, GlobalRecord::Kind::Function, Avail, D,
|
|
*Access, getFlags(WeakDef));
|
|
Ctx.Verifier->verify(CompleteGR, CompleteFA);
|
|
|
|
if (Dtor->isVirtual()) {
|
|
Name = getMangledCtorDtor(M, Dtor_Deleting);
|
|
auto [VirtualGR, VirtualFA] = Ctx.Slice->addGlobal(
|
|
Name, RecordLinkage::Exported, GlobalRecord::Kind::Function, Avail,
|
|
D, *Access, getFlags(WeakDef));
|
|
Ctx.Verifier->verify(VirtualGR, VirtualFA);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// Though abstract methods can map to exports, this is generally unexpected.
|
|
// Except in the case of destructors. Only ignore pure virtuals after
|
|
// checking if the member function was a destructor.
|
|
if (M->isPureVirtual())
|
|
continue;
|
|
|
|
std::string Name = getMangledName(M);
|
|
auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
|
|
GlobalRecord::Kind::Function, Avail, M,
|
|
*Access, getFlags(WeakDef));
|
|
Ctx.Verifier->verify(GR, FA);
|
|
}
|
|
|
|
if (auto *Templ = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
|
|
if (!Templ->isExplicitInstantiationOrSpecialization())
|
|
return true;
|
|
}
|
|
|
|
using var_iter = CXXRecordDecl::specific_decl_iterator<VarDecl>;
|
|
using var_range = iterator_range<var_iter>;
|
|
for (const auto *Var : var_range(D->decls())) {
|
|
// Skip const static member variables.
|
|
// \code
|
|
// struct S {
|
|
// static const int x = 0;
|
|
// };
|
|
// \endcode
|
|
if (Var->isStaticDataMember() && Var->hasInit())
|
|
continue;
|
|
|
|
// Skip unexported var decls.
|
|
if (!isExported(Var))
|
|
continue;
|
|
|
|
const std::string Name = getMangledName(Var);
|
|
const auto Access = getAccessForDecl(Var);
|
|
if (!Access)
|
|
return true;
|
|
const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(Var);
|
|
const bool WeakDef = Var->hasAttr<WeakAttr>() || KeepInlineAsWeak;
|
|
|
|
auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
|
|
GlobalRecord::Kind::Variable, Avail, D,
|
|
*Access, getFlags(WeakDef));
|
|
Ctx.Verifier->verify(GR, FA);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace clang::installapi
|