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