xref: /llvm-project/clang/lib/InstallAPI/Visitor.cpp (revision 50ae8a2a38b618d76193bed04b1d7df6890d5c8a)
1 //===- Visitor.cpp ---------------------------------------------*- C++ -*-===//
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "clang/InstallAPI/Visitor.h"
10 #include "clang/AST/ParentMapContext.h"
11 #include "clang/Basic/Linkage.h"
12 #include "clang/InstallAPI/Frontend.h"
13 #include "llvm/ADT/SmallString.h"
14 #include "llvm/ADT/StringRef.h"
15 #include "llvm/IR/DataLayout.h"
16 #include "llvm/IR/Mangler.h"
17 
18 using namespace llvm;
19 using namespace llvm::MachO;
20 
21 namespace clang::installapi {
22 
23 // Exported NamedDecl needs to have external linkage and
24 // default visibility from LinkageComputer.
25 static bool isExported(const NamedDecl *D) {
26   auto LV = D->getLinkageAndVisibility();
27   return isExternallyVisible(LV.getLinkage()) &&
28          (LV.getVisibility() == DefaultVisibility);
29 }
30 
31 static bool isInlined(const FunctionDecl *D) {
32   bool HasInlineAttribute = false;
33   bool NoCXXAttr =
34       (!D->getASTContext().getLangOpts().CPlusPlus &&
35        !D->getASTContext().getTargetInfo().getCXXABI().isMicrosoft() &&
36        !D->hasAttr<DLLExportAttr>());
37 
38   // Check all redeclarations to find an inline attribute or keyword.
39   for (const auto *RD : D->redecls()) {
40     if (!RD->isInlined())
41       continue;
42     HasInlineAttribute = true;
43     if (!(NoCXXAttr || RD->hasAttr<GNUInlineAttr>()))
44       continue;
45     if (RD->doesThisDeclarationHaveABody() &&
46         RD->isInlineDefinitionExternallyVisible())
47       return false;
48   }
49 
50   if (!HasInlineAttribute)
51     return false;
52 
53   return true;
54 }
55 
56 static SymbolFlags getFlags(bool WeakDef, bool ThreadLocal) {
57   SymbolFlags Result = SymbolFlags::None;
58   if (WeakDef)
59     Result |= SymbolFlags::WeakDefined;
60   if (ThreadLocal)
61     Result |= SymbolFlags::ThreadLocalValue;
62 
63   return Result;
64 }
65 
66 void InstallAPIVisitor::HandleTranslationUnit(ASTContext &ASTCtx) {
67   if (ASTCtx.getDiagnostics().hasErrorOccurred())
68     return;
69 
70   auto *D = ASTCtx.getTranslationUnitDecl();
71   TraverseDecl(D);
72 }
73 
74 std::string InstallAPIVisitor::getMangledName(const NamedDecl *D) const {
75   SmallString<256> Name;
76   if (MC->shouldMangleDeclName(D)) {
77     raw_svector_ostream NStream(Name);
78     MC->mangleName(D, NStream);
79   } else
80     Name += D->getNameAsString();
81 
82   return getBackendMangledName(Name);
83 }
84 
85 std::string InstallAPIVisitor::getBackendMangledName(Twine Name) const {
86   SmallString<256> FinalName;
87   Mangler::getNameWithPrefix(FinalName, Name, DataLayout(Layout));
88   return std::string(FinalName);
89 }
90 
91 std::optional<HeaderType>
92 InstallAPIVisitor::getAccessForDecl(const NamedDecl *D) const {
93   SourceLocation Loc = D->getLocation();
94   if (Loc.isInvalid())
95     return std::nullopt;
96 
97   // If the loc refers to a macro expansion, InstallAPI needs to first get the
98   // file location of the expansion.
99   auto FileLoc = SrcMgr.getFileLoc(Loc);
100   FileID ID = SrcMgr.getFileID(FileLoc);
101   if (ID.isInvalid())
102     return std::nullopt;
103 
104   const FileEntry *FE = SrcMgr.getFileEntryForID(ID);
105   if (!FE)
106     return std::nullopt;
107 
108   auto Header = Ctx.findAndRecordFile(FE, PP);
109   if (!Header.has_value())
110     return std::nullopt;
111 
112   HeaderType Access = Header.value();
113   assert(Access != HeaderType::Unknown && "unexpected access level for global");
114   return Access;
115 }
116 
117 /// Check if the interface itself or any of its super classes have an
118 /// exception attribute. InstallAPI needs to export an additional symbol
119 /// ("OBJC_EHTYPE_$CLASS_NAME") if any of the classes have the exception
120 /// attribute.
121 static bool hasObjCExceptionAttribute(const ObjCInterfaceDecl *D) {
122   for (; D != nullptr; D = D->getSuperClass())
123     if (D->hasAttr<ObjCExceptionAttr>())
124       return true;
125 
126   return false;
127 }
128 void InstallAPIVisitor::recordObjCInstanceVariables(
129     const ASTContext &ASTCtx, ObjCContainerRecord *Record, StringRef SuperClass,
130     const llvm::iterator_range<
131         DeclContext::specific_decl_iterator<ObjCIvarDecl>>
132         Ivars) {
133   RecordLinkage Linkage = RecordLinkage::Exported;
134   const RecordLinkage ContainerLinkage = Record->getLinkage();
135   // If fragile, set to unknown.
136   if (ASTCtx.getLangOpts().ObjCRuntime.isFragile())
137     Linkage = RecordLinkage::Unknown;
138   // Linkage should be inherited from container.
139   else if (ContainerLinkage != RecordLinkage::Unknown)
140     Linkage = ContainerLinkage;
141   for (const auto *IV : Ivars) {
142     auto Access = getAccessForDecl(IV);
143     if (!Access)
144       continue;
145     StringRef Name = IV->getName();
146     const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(IV);
147     auto AC = IV->getCanonicalAccessControl();
148     Ctx.Slice->addObjCIVar(Record, Name, Linkage, Avail, IV, *Access, AC);
149   }
150 }
151 
152 bool InstallAPIVisitor::VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) {
153   // Skip forward declaration for classes (@class)
154   if (!D->isThisDeclarationADefinition())
155     return true;
156 
157   // Skip over declarations that access could not be collected for.
158   auto Access = getAccessForDecl(D);
159   if (!Access)
160     return true;
161 
162   StringRef Name = D->getObjCRuntimeNameAsString();
163   const RecordLinkage Linkage =
164       isExported(D) ? RecordLinkage::Exported : RecordLinkage::Internal;
165   const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
166   const bool IsEHType =
167       (!D->getASTContext().getLangOpts().ObjCRuntime.isFragile() &&
168        hasObjCExceptionAttribute(D));
169 
170   ObjCInterfaceRecord *Class =
171       Ctx.Slice->addObjCInterface(Name, Linkage, Avail, D, *Access, IsEHType);
172 
173   // Get base class.
174   StringRef SuperClassName;
175   if (const auto *SuperClass = D->getSuperClass())
176     SuperClassName = SuperClass->getObjCRuntimeNameAsString();
177 
178   recordObjCInstanceVariables(D->getASTContext(), Class, SuperClassName,
179                               D->ivars());
180   return true;
181 }
182 
183 bool InstallAPIVisitor::VisitObjCCategoryDecl(const ObjCCategoryDecl *D) {
184   StringRef CategoryName = D->getName();
185   // Skip over declarations that access could not be collected for.
186   auto Access = getAccessForDecl(D);
187   if (!Access)
188     return true;
189   const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
190   const ObjCInterfaceDecl *InterfaceD = D->getClassInterface();
191   const StringRef InterfaceName = InterfaceD->getName();
192 
193   ObjCCategoryRecord *Category = Ctx.Slice->addObjCCategory(
194       InterfaceName, CategoryName, Avail, D, *Access);
195   recordObjCInstanceVariables(D->getASTContext(), Category, InterfaceName,
196                               D->ivars());
197   return true;
198 }
199 
200 bool InstallAPIVisitor::VisitVarDecl(const VarDecl *D) {
201   // Skip function parameters.
202   if (isa<ParmVarDecl>(D))
203     return true;
204 
205   // Skip variables in records. They are handled seperately for C++.
206   if (D->getDeclContext()->isRecord())
207     return true;
208 
209   // Skip anything inside functions or methods.
210   if (!D->isDefinedOutsideFunctionOrMethod())
211     return true;
212 
213   // If this is a template but not specialization or instantiation, skip.
214   if (D->getASTContext().getTemplateOrSpecializationInfo(D) &&
215       D->getTemplateSpecializationKind() == TSK_Undeclared)
216     return true;
217 
218   // Skip over declarations that access could not collected for.
219   auto Access = getAccessForDecl(D);
220   if (!Access)
221     return true;
222 
223   const RecordLinkage Linkage =
224       isExported(D) ? RecordLinkage::Exported : RecordLinkage::Internal;
225   const bool WeakDef = D->hasAttr<WeakAttr>();
226   const bool ThreadLocal = D->getTLSKind() != VarDecl::TLS_None;
227   const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
228   Ctx.Slice->addGlobal(getMangledName(D), Linkage, GlobalRecord::Kind::Variable,
229                        Avail, D, *Access, getFlags(WeakDef, ThreadLocal));
230   return true;
231 }
232 
233 bool InstallAPIVisitor::VisitFunctionDecl(const FunctionDecl *D) {
234   if (const CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(D)) {
235     // Skip member function in class templates.
236     if (M->getParent()->getDescribedClassTemplate() != nullptr)
237       return true;
238 
239     // Skip methods in CXX RecordDecls.
240     for (auto P : D->getASTContext().getParents(*M)) {
241       if (P.get<CXXRecordDecl>())
242         return true;
243     }
244 
245     // Skip CXX ConstructorDecls and DestructorDecls.
246     if (isa<CXXConstructorDecl>(M) || isa<CXXDestructorDecl>(M))
247       return true;
248   }
249 
250   // Skip templated functions.
251   switch (D->getTemplatedKind()) {
252   case FunctionDecl::TK_NonTemplate:
253   case FunctionDecl::TK_DependentNonTemplate:
254     break;
255   case FunctionDecl::TK_MemberSpecialization:
256   case FunctionDecl::TK_FunctionTemplateSpecialization:
257     if (auto *TempInfo = D->getTemplateSpecializationInfo()) {
258       if (!TempInfo->isExplicitInstantiationOrSpecialization())
259         return true;
260     }
261     break;
262   case FunctionDecl::TK_FunctionTemplate:
263   case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
264     return true;
265   }
266 
267   auto Access = getAccessForDecl(D);
268   if (!Access)
269     return true;
270   auto Name = getMangledName(D);
271   const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
272   const bool ExplicitInstantiation = D->getTemplateSpecializationKind() ==
273                                      TSK_ExplicitInstantiationDeclaration;
274   const bool WeakDef = ExplicitInstantiation || D->hasAttr<WeakAttr>();
275   const bool Inlined = isInlined(D);
276   const RecordLinkage Linkage = (Inlined || !isExported(D))
277                                     ? RecordLinkage::Internal
278                                     : RecordLinkage::Exported;
279   Ctx.Slice->addGlobal(Name, Linkage, GlobalRecord::Kind::Function, Avail, D,
280                        *Access, getFlags(WeakDef, /*ThreadLocal=*/false),
281                        Inlined);
282   return true;
283 }
284 
285 } // namespace clang::installapi
286