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();
+}