/* * 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; 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>"; } } if (mangler->shouldMangleDeclName(decl)) { std::string mangled; llvm::raw_string_ostream ss(mangled); mangler->mangleName(decl, ss); return mangled; } if (auto identifier = decl->getIdentifier()) { return identifier->getName(); } return "<error>"; } bool VisitDecl(Decl* decl) { // 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; } DeclarationType declaration_type; std::string declaration_name = getDeclName(named_decl); bool is_extern = named_decl->getFormalLinkage() == ExternalLinkage; bool is_definition = false; bool no_guard = false; if (auto function_decl = dyn_cast<FunctionDecl>(decl)) { declaration_type = DeclarationType::function; is_definition = function_decl->isThisDeclarationADefinition(); } else if (auto var_decl = dyn_cast<VarDecl>(decl)) { if (!var_decl->isFileVarDecl()) { return true; } declaration_type = DeclarationType::variable; 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; } auto start_loc = src_manager.getPresumedLoc(decl->getLocStart()); auto end_loc = src_manager.getPresumedLoc(decl->getLocEnd()); Location location = { .filename = start_loc.getFilename(), .start = { .line = start_loc.getLine(), .column = start_loc.getColumn(), }, .end = { .line = end_loc.getLine(), .column = end_loc.getColumn(), } }; 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 == "introduced_in_future") { // Tag the compiled-for arch, since this can vary across archs. availability.arch_availability[type.arch].future = 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_mips", { &arch_availability[Arch::mips].introduced } }, { "introduced_in_x86", { &arch_availability[Arch::x86].introduced } }, { "introduced_in_32", { &arch_availability[Arch::arm].introduced, &arch_availability[Arch::mips].introduced, &arch_availability[Arch::x86].introduced } }, { "introduced_in_64", { &arch_availability[Arch::arm64].introduced, &arch_availability[Arch::mips64].introduced, &arch_availability[Arch::x86_64].introduced } }, }; if (auto it = prefix_map.find(fragments[0]); 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 dummy; std::tie(symbol_it, dummy) = database.symbols.insert({ declaration_name, symbol }); } // 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) { 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.availability.insert(std::make_pair(type, availability)); symbol_it->second.declarations.insert(std::make_pair(location, declaration)); } 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.future) { ss << "future, "; } 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(); }