| /* | 
 |  * Copyright (C) 2016 The Android Open Source Project | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 |  | 
 | #include "DeclarationDatabase.h" | 
 |  | 
 | #include <err.h> | 
 |  | 
 | #include <iostream> | 
 | #include <map> | 
 | #include <mutex> | 
 | #include <set> | 
 | #include <sstream> | 
 | #include <string> | 
 | #include <utility> | 
 |  | 
 | #include <clang/AST/AST.h> | 
 | #include <clang/AST/Attr.h> | 
 | #include <clang/AST/Mangle.h> | 
 | #include <clang/AST/RecursiveASTVisitor.h> | 
 | #include <clang/Frontend/ASTUnit.h> | 
 | #include <llvm/Support/raw_ostream.h> | 
 |  | 
 | using namespace clang; | 
 |  | 
 | static bool shouldMangle(MangleContext* mangler, NamedDecl* decl) { | 
 |   // Passing a decl with static linkage to the mangler gives incorrect results. | 
 |   // Check some things ourselves before handing it off to the mangler. | 
 |   if (auto FD = dyn_cast<FunctionDecl>(decl)) { | 
 |     if (FD->isExternC()) { | 
 |       return false; | 
 |     } | 
 |  | 
 |     if (FD->isInExternCContext()) { | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   return mangler->shouldMangleDeclName(decl); | 
 | } | 
 |  | 
 | class Visitor : public RecursiveASTVisitor<Visitor> { | 
 |   HeaderDatabase& database; | 
 |   CompilationType type; | 
 |   SourceManager& src_manager; | 
 |   std::unique_ptr<MangleContext> mangler; | 
 |  | 
 |  public: | 
 |   Visitor(HeaderDatabase& database, CompilationType type, ASTContext& ctx) | 
 |       : database(database), type(type), src_manager(ctx.getSourceManager()) { | 
 |     mangler.reset(ItaniumMangleContext::create(ctx, ctx.getDiagnostics())); | 
 |   } | 
 |  | 
 |   std::string getDeclName(NamedDecl* decl) { | 
 |     if (auto var_decl = dyn_cast<VarDecl>(decl)) { | 
 |       if (!var_decl->isFileVarDecl()) { | 
 |         return "<local var>"; | 
 |       } | 
 |     } | 
 |  | 
 |     // <math.h> maps fool onto foo on 32-bit, since long double is the same as double. | 
 |     if (auto asm_attr = decl->getAttr<AsmLabelAttr>()) { | 
 |       return asm_attr->getLabel().str(); | 
 |     } | 
 |  | 
 |     // The decl might not have a name (e.g. bitfields). | 
 |     if (auto identifier = decl->getIdentifier()) { | 
 |       if (shouldMangle(mangler.get(), decl)) { | 
 |         std::string mangled; | 
 |         llvm::raw_string_ostream ss(mangled); | 
 |         mangler->mangleName(decl, ss); | 
 |         return mangled; | 
 |       } | 
 |  | 
 |       return identifier->getName().str(); | 
 |     } | 
 |  | 
 |     return "<unnamed>"; | 
 |   } | 
 |  | 
 |   bool VisitDeclaratorDecl(DeclaratorDecl* decl, SourceRange range) { | 
 |     // Skip declarations inside of functions (function arguments, variable declarations inside of | 
 |     // inline functions, etc). | 
 |     if (decl->getParentFunctionOrMethod()) { | 
 |       return true; | 
 |     } | 
 |  | 
 |     auto named_decl = dyn_cast<NamedDecl>(decl); | 
 |     if (!named_decl) { | 
 |       return true; | 
 |     } | 
 |  | 
 |     std::string declaration_name = getDeclName(named_decl); | 
 |     bool is_extern = named_decl->getFormalLinkage() == ExternalLinkage; | 
 |     bool is_definition = false; | 
 |     bool no_guard = false; | 
 |     bool fortify_inline = false; | 
 |  | 
 |     if (auto function_decl = dyn_cast<FunctionDecl>(decl)) { | 
 |       is_definition = function_decl->isThisDeclarationADefinition(); | 
 |     } else if (auto var_decl = dyn_cast<VarDecl>(decl)) { | 
 |       if (!var_decl->isFileVarDecl()) { | 
 |         return true; | 
 |       } | 
 |  | 
 |       switch (var_decl->isThisDeclarationADefinition()) { | 
 |         case VarDecl::DeclarationOnly: | 
 |           is_definition = false; | 
 |           break; | 
 |  | 
 |         case VarDecl::Definition: | 
 |           is_definition = true; | 
 |           break; | 
 |  | 
 |         case VarDecl::TentativeDefinition: | 
 |           // Forbid tentative definitions in headers. | 
 |           fprintf(stderr, "ERROR: declaration '%s' is a tentative definition\n", | 
 |                   declaration_name.c_str()); | 
 |           decl->dump(); | 
 |           abort(); | 
 |       } | 
 |     } else { | 
 |       // We only care about function and variable declarations. | 
 |       return true; | 
 |     } | 
 |  | 
 |     if (decl->hasAttr<UnavailableAttr>()) { | 
 |       // Skip declarations that exist only for compile-time diagnostics. | 
 |       return true; | 
 |     } | 
 |  | 
 |     DeclarationAvailability availability; | 
 |  | 
 |     // Find and parse __ANDROID_AVAILABILITY_DUMP__ annotations. | 
 |     for (const AnnotateAttr* attr : decl->specific_attrs<AnnotateAttr>()) { | 
 |       llvm::StringRef annotation = attr->getAnnotation(); | 
 |       if (annotation == "versioner_no_guard") { | 
 |         no_guard = true; | 
 |       } else if (annotation == "versioner_fortify_inline") { | 
 |         fortify_inline = true; | 
 |       } else { | 
 |         llvm::SmallVector<llvm::StringRef, 2> fragments; | 
 |         annotation.split(fragments, "="); | 
 |         if (fragments.size() != 2) { | 
 |           continue; | 
 |         } | 
 |  | 
 |         auto& global_availability = availability.global_availability; | 
 |         auto& arch_availability = availability.arch_availability; | 
 |         std::map<std::string, std::vector<int*>> prefix_map = { | 
 |           { "introduced_in", { &global_availability.introduced } }, | 
 |           { "deprecated_in", { &global_availability.deprecated } }, | 
 |           { "obsoleted_in", { &global_availability.obsoleted } }, | 
 |           { "introduced_in_arm", { &arch_availability[Arch::arm].introduced } }, | 
 |           { "introduced_in_x86", { &arch_availability[Arch::x86].introduced } }, | 
 |           { "introduced_in_32", | 
 |             { &arch_availability[Arch::arm].introduced, | 
 |               &arch_availability[Arch::x86].introduced } }, | 
 |           { "introduced_in_64", | 
 |             { &arch_availability[Arch::arm64].introduced, | 
 |               &arch_availability[Arch::riscv64].introduced, | 
 |               &arch_availability[Arch::x86_64].introduced } }, | 
 |         }; | 
 |  | 
 |         if (auto it = prefix_map.find(fragments[0].str()); it != prefix_map.end()) { | 
 |           int value; | 
 |           if (fragments[1].getAsInteger(10, value)) { | 
 |             errx(1, "invalid __ANDROID_AVAILABILITY_DUMP__ annotation: '%s'", | 
 |                  annotation.str().c_str()); | 
 |           } | 
 |  | 
 |           for (int* ptr : it->second) { | 
 |             *ptr = value; | 
 |           } | 
 |         } | 
 |       } | 
 |     } | 
 |  | 
 |     auto symbol_it = database.symbols.find(declaration_name); | 
 |     if (symbol_it == database.symbols.end()) { | 
 |       Symbol symbol = {.name = declaration_name }; | 
 |       bool unused; | 
 |       std::tie(symbol_it, unused) = database.symbols.insert({declaration_name, symbol}); | 
 |     } | 
 |  | 
 |     auto expansion_range = src_manager.getExpansionRange(range); | 
 |     auto filename = src_manager.getFilename(expansion_range.getBegin()); | 
 |     if (filename != src_manager.getFilename(expansion_range.getEnd())) { | 
 |       errx(1, "expansion range filenames don't match"); | 
 |     } | 
 |  | 
 |     Location location = { | 
 |       .filename = filename.str(), | 
 |       .start = { | 
 |         .line = src_manager.getExpansionLineNumber(expansion_range.getBegin()), | 
 |         .column = src_manager.getExpansionColumnNumber(expansion_range.getBegin()), | 
 |       }, | 
 |       .end = { | 
 |         .line = src_manager.getExpansionLineNumber(expansion_range.getEnd()), | 
 |         .column = src_manager.getExpansionColumnNumber(expansion_range.getEnd()), | 
 |       } | 
 |     }; | 
 |  | 
 |     // Find or insert an entry for the declaration. | 
 |     if (auto declaration_it = symbol_it->second.declarations.find(location); | 
 |         declaration_it != symbol_it->second.declarations.end()) { | 
 |       if (declaration_it->second.is_extern != is_extern || | 
 |           declaration_it->second.is_definition != is_definition || | 
 |           declaration_it->second.no_guard != no_guard || | 
 |           declaration_it->second.fortify_inline != fortify_inline) { | 
 |         errx(1, "varying declaration of '%s' at %s:%u:%u", declaration_name.c_str(), | 
 |              location.filename.c_str(), location.start.line, location.start.column); | 
 |       } | 
 |       declaration_it->second.availability.insert(std::make_pair(type, availability)); | 
 |     } else { | 
 |       Declaration declaration; | 
 |       declaration.name = declaration_name; | 
 |       declaration.location = location; | 
 |       declaration.is_extern = is_extern; | 
 |       declaration.is_definition = is_definition; | 
 |       declaration.no_guard = no_guard; | 
 |       declaration.fortify_inline = fortify_inline; | 
 |       declaration.availability.insert(std::make_pair(type, availability)); | 
 |       symbol_it->second.declarations.insert(std::make_pair(location, declaration)); | 
 |     } | 
 |  | 
 |     return true; | 
 |   } | 
 |  | 
 |   bool VisitDeclaratorDecl(DeclaratorDecl* decl) { | 
 |     return VisitDeclaratorDecl(decl, decl->getSourceRange()); | 
 |   } | 
 |  | 
 |   bool TraverseLinkageSpecDecl(LinkageSpecDecl* decl) { | 
 |     // Make sure that we correctly calculate the SourceRange of a declaration that has a non-braced | 
 |     // extern "C"/"C++". | 
 |     if (!decl->hasBraces()) { | 
 |       DeclaratorDecl* child = nullptr; | 
 |       for (auto child_decl : decl->decls()) { | 
 |         if (child != nullptr) { | 
 |           errx(1, "LinkageSpecDecl has multiple children"); | 
 |         } | 
 |  | 
 |         if (DeclaratorDecl* declarator_decl = dyn_cast<DeclaratorDecl>(child_decl)) { | 
 |           child = declarator_decl; | 
 |         } else { | 
 |           errx(1, "child of LinkageSpecDecl is not a DeclaratorDecl"); | 
 |         } | 
 |       } | 
 |  | 
 |       return VisitDeclaratorDecl(child, decl->getSourceRange()); | 
 |     } | 
 |  | 
 |     for (auto child : decl->decls()) { | 
 |       if (!TraverseDecl(child)) { | 
 |         return false; | 
 |       } | 
 |     } | 
 |     return true; | 
 |   } | 
 | }; | 
 |  | 
 | bool DeclarationAvailability::merge(const DeclarationAvailability& other) { | 
 | #define check_avail(expr) error |= (!this->expr.empty() && this->expr != other.expr); | 
 |   bool error = false; | 
 |  | 
 |   if (!other.global_availability.empty()) { | 
 |     check_avail(global_availability); | 
 |     this->global_availability = other.global_availability; | 
 |   } | 
 |  | 
 |   for (Arch arch : supported_archs) { | 
 |     if (!other.arch_availability[arch].empty()) { | 
 |       check_avail(arch_availability[arch]); | 
 |       this->arch_availability[arch] = other.arch_availability[arch]; | 
 |     } | 
 |   } | 
 | #undef check_avail | 
 |  | 
 |   return !error; | 
 | } | 
 |  | 
 | bool Declaration::calculateAvailability(DeclarationAvailability* output) const { | 
 |   DeclarationAvailability avail; | 
 |   for (const auto& it : this->availability) { | 
 |     if (!avail.merge(it.second)) { | 
 |       return false; | 
 |     } | 
 |   } | 
 |   *output = avail; | 
 |   return true; | 
 | } | 
 |  | 
 | bool Symbol::calculateAvailability(DeclarationAvailability* output) const { | 
 |   DeclarationAvailability avail; | 
 |   for (const auto& it : this->declarations) { | 
 |     // Don't merge availability for inline functions (because they shouldn't have any). | 
 |     if (it.second.is_definition) { | 
 |       continue; | 
 |     } | 
 |  | 
 |     DeclarationAvailability decl_availability; | 
 |     if (!it.second.calculateAvailability(&decl_availability)) { | 
 |       return false; | 
 |       abort(); | 
 |     } | 
 |  | 
 |     if (!avail.merge(decl_availability)) { | 
 |       return false; | 
 |     } | 
 |   } | 
 |   *output = avail; | 
 |   return true; | 
 | } | 
 |  | 
 | bool Symbol::hasDeclaration(const CompilationType& type) const { | 
 |   for (const auto& decl_it : this->declarations) { | 
 |     for (const auto& compilation_it : decl_it.second.availability) { | 
 |       if (compilation_it.first == type) { | 
 |         return true; | 
 |       } | 
 |     } | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | void HeaderDatabase::parseAST(CompilationType type, ASTContext& ctx) { | 
 |   std::unique_lock<std::mutex> lock(this->mutex); | 
 |   Visitor visitor(*this, type, ctx); | 
 |   visitor.TraverseDecl(ctx.getTranslationUnitDecl()); | 
 | } | 
 |  | 
 | std::string to_string(const AvailabilityValues& av) { | 
 |   std::stringstream ss; | 
 |  | 
 |   if (av.introduced != 0) { | 
 |     ss << "introduced = " << av.introduced << ", "; | 
 |   } | 
 |  | 
 |   if (av.deprecated != 0) { | 
 |     ss << "deprecated = " << av.deprecated << ", "; | 
 |   } | 
 |  | 
 |   if (av.obsoleted != 0) { | 
 |     ss << "obsoleted = " << av.obsoleted << ", "; | 
 |   } | 
 |  | 
 |   std::string result = ss.str(); | 
 |   if (!result.empty()) { | 
 |     result = result.substr(0, result.length() - 2); | 
 |   } | 
 |   return result; | 
 | } | 
 |  | 
 | std::string to_string(const DeclarationType& type) { | 
 |   switch (type) { | 
 |     case DeclarationType::function: | 
 |       return "function"; | 
 |     case DeclarationType::variable: | 
 |       return "variable"; | 
 |     case DeclarationType::inconsistent: | 
 |       return "inconsistent"; | 
 |   } | 
 |   abort(); | 
 | } | 
 |  | 
 | std::string to_string(const DeclarationAvailability& decl_av) { | 
 |   std::stringstream ss; | 
 |   if (!decl_av.global_availability.empty()) { | 
 |     ss << to_string(decl_av.global_availability) << ", "; | 
 |   } | 
 |  | 
 |   for (const auto& it : decl_av.arch_availability) { | 
 |     if (!it.second.empty()) { | 
 |       ss << to_string(it.first) << ": " << to_string(it.second) << ", "; | 
 |     } | 
 |   } | 
 |  | 
 |   std::string result = ss.str(); | 
 |   if (result.size() == 0) { | 
 |     return "no availability"; | 
 |   } | 
 |  | 
 |   return result.substr(0, result.length() - 2); | 
 | } | 
 |  | 
 | std::string to_string(const Location& loc) { | 
 |   std::stringstream ss; | 
 |   ss << loc.filename << ":" << loc.start.line << ":" << loc.start.column; | 
 |   return ss.str(); | 
 | } |