versioner: replace availability attributes with annotate.
Major refactor to use __attribute__((annotate)) to be able to keep
track of the semantic differences between __INTRODUCED_IN(x) and
__INTRODUCED_IN_X86(x), for use in the upcoming preprocessor.
Bug: http://b/30170081
Change-Id: I6496a8c40ba7f4553de9a2be0bbddcf37c813937
diff --git a/tools/versioner/src/DeclarationDatabase.cpp b/tools/versioner/src/DeclarationDatabase.cpp
index 9f02588..85ad9ba 100644
--- a/tools/versioner/src/DeclarationDatabase.cpp
+++ b/tools/versioner/src/DeclarationDatabase.cpp
@@ -16,10 +16,15 @@
#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>
@@ -32,12 +37,13 @@
class Visitor : public RecursiveASTVisitor<Visitor> {
HeaderDatabase& database;
+ CompilationType type;
SourceManager& src_manager;
std::unique_ptr<MangleContext> mangler;
public:
- Visitor(HeaderDatabase& database, ASTContext& ctx)
- : database(database), src_manager(ctx.getSourceManager()) {
+ Visitor(HeaderDatabase& database, CompilationType type, ASTContext& ctx)
+ : database(database), type(type), src_manager(ctx.getSourceManager()) {
mangler.reset(ItaniumMangleContext::create(ctx, ctx.getDiagnostics()));
}
@@ -114,66 +120,237 @@
return true;
}
- // Look for availability annotations.
+ 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;
- for (const AvailabilityAttr* attr : decl->specific_attrs<AvailabilityAttr>()) {
- if (attr->getPlatform()->getName() != "android") {
- fprintf(stderr, "skipping non-android platform %s\n",
- attr->getPlatform()->getName().str().c_str());
- continue;
+
+ // Find and parse __ANDROID_AVAILABILITY_DUMP__ annotations.
+ for (const AnnotateAttr* attr : decl->specific_attrs<AnnotateAttr>()) {
+ llvm::StringRef annotation = attr->getAnnotation();
+ 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 } },
+ };
+
+ auto it = prefix_map.find(fragments[0]);
+ if (it == prefix_map.end()) {
+ continue;
+ }
+ 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;
+ }
}
- if (attr->getIntroduced().getMajor() != 0) {
- availability.introduced = attr->getIntroduced().getMajor();
- }
- if (attr->getDeprecated().getMajor() != 0) {
- availability.deprecated = attr->getDeprecated().getMajor();
- }
- if (attr->getObsoleted().getMajor() != 0) {
- availability.obsoleted = attr->getObsoleted().getMajor();
- }
+ }
+
+ 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.
- auto declaration_it = database.declarations.find(declaration_name);
- if (declaration_it == database.declarations.end()) {
- Declaration declaration = {.name = declaration_name };
- bool inserted;
- std::tie(declaration_it, inserted) =
- database.declarations.insert({ declaration_name, declaration });
- }
-
- auto& declaration_locations = declaration_it->second.locations;
- auto presumed_loc = src_manager.getPresumedLoc(decl->getLocation());
- DeclarationLocation location = {
- .filename = presumed_loc.getFilename(),
- .line_number = presumed_loc.getLine(),
- .column = presumed_loc.getColumn(),
- .type = declaration_type,
- .is_extern = is_extern,
- .is_definition = is_definition,
- .availability = availability,
- };
-
- // It's fine if the location is already there, we'll get an iterator to the existing element.
- auto location_it = declaration_locations.begin();
- bool inserted = false;
- std::tie(location_it, inserted) = declaration_locations.insert(location);
-
- // If we didn't insert, check to see if the availability attributes are identical.
- if (!inserted) {
- if (location_it->availability != availability) {
- fprintf(stderr, "ERROR: availability attribute mismatch\n");
- decl->dump();
- abort();
+ auto declaration_it = symbol_it->second.declarations.find(location);
+ if (declaration_it == symbol_it->second.declarations.end()) {
+ Declaration declaration;
+ declaration.location = location;
+ declaration.is_extern = is_extern;
+ declaration.is_definition = is_definition;
+ declaration.availability.insert(std::make_pair(type, availability));
+ symbol_it->second.declarations.insert(std::make_pair(location, declaration));
+ } else {
+ if (declaration_it->second.is_extern != is_extern ||
+ declaration_it->second.is_definition != is_definition) {
+ 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));
}
return true;
}
};
-void HeaderDatabase::parseAST(ASTUnit* ast) {
+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, ASTUnit* ast) {
+ std::unique_lock<std::mutex> lock(this->mutex);
ASTContext& ctx = ast->getASTContext();
- Visitor visitor(*this, ctx);
+ Visitor visitor(*this, type, ctx);
visitor.TraverseDecl(ctx.getTranslationUnitDecl());
}
+
+std::string to_string(const CompilationType& type) {
+ std::stringstream ss;
+ ss << to_string(type.arch) << "-" << type.api_level;
+ return ss.str();
+}
+
+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 (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();
+}