Implement VerifierDeps encoding/decoding

This patch implements serialization and deserialization of the data
recorded by VerifierDeps.

Test: m test-art-host-gtest-verifier_deps_test
Bug: 30937355
Change-Id: I19320b8e70d5c5128653d09a5cdb5b6f677a2f2d
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index 4953483..350c838 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -17,6 +17,7 @@
 #include "verifier_deps.h"
 
 #include "compiler_callbacks.h"
+#include "leb128.h"
 #include "mirror/class-inl.h"
 #include "runtime.h"
 
@@ -317,5 +318,152 @@
   }
 }
 
+static inline uint32_t DecodeUint32WithOverflowCheck(const uint8_t** in, const uint8_t* end) {
+  CHECK_LT(*in, end);
+  return DecodeUnsignedLeb128(in);
+}
+
+template<typename T1, typename T2>
+static inline void EncodeTuple(std::vector<uint8_t>* out, const std::tuple<T1, T2>& t) {
+  EncodeUnsignedLeb128(out, std::get<0>(t));
+  EncodeUnsignedLeb128(out, std::get<1>(t));
+}
+
+template<typename T1, typename T2>
+static inline void DecodeTuple(const uint8_t** in, const uint8_t* end, std::tuple<T1, T2>* t) {
+  T1 v1 = static_cast<T1>(DecodeUint32WithOverflowCheck(in, end));
+  T2 v2 = static_cast<T2>(DecodeUint32WithOverflowCheck(in, end));
+  *t = std::make_tuple(v1, v2);
+}
+
+template<typename T1, typename T2, typename T3>
+static inline void EncodeTuple(std::vector<uint8_t>* out, const std::tuple<T1, T2, T3>& t) {
+  EncodeUnsignedLeb128(out, std::get<0>(t));
+  EncodeUnsignedLeb128(out, std::get<1>(t));
+  EncodeUnsignedLeb128(out, std::get<2>(t));
+}
+
+template<typename T1, typename T2, typename T3>
+static inline void DecodeTuple(const uint8_t** in, const uint8_t* end, std::tuple<T1, T2, T3>* t) {
+  T1 v1 = static_cast<T1>(DecodeUint32WithOverflowCheck(in, end));
+  T2 v2 = static_cast<T2>(DecodeUint32WithOverflowCheck(in, end));
+  T3 v3 = static_cast<T2>(DecodeUint32WithOverflowCheck(in, end));
+  *t = std::make_tuple(v1, v2, v3);
+}
+
+template<typename T>
+static inline void EncodeSet(std::vector<uint8_t>* out, const std::set<T>& set) {
+  EncodeUnsignedLeb128(out, set.size());
+  for (const T& entry : set) {
+    EncodeTuple(out, entry);
+  }
+}
+
+template<typename T>
+static inline void DecodeSet(const uint8_t** in, const uint8_t* end, std::set<T>* set) {
+  DCHECK(set->empty());
+  size_t num_entries = DecodeUint32WithOverflowCheck(in, end);
+  for (size_t i = 0; i < num_entries; ++i) {
+    T tuple;
+    DecodeTuple(in, end, &tuple);
+    set->emplace(tuple);
+  }
+}
+
+static inline void EncodeStringVector(std::vector<uint8_t>* out,
+                                      const std::vector<std::string>& strings) {
+  EncodeUnsignedLeb128(out, strings.size());
+  for (const std::string& str : strings) {
+    const uint8_t* data = reinterpret_cast<const uint8_t*>(str.c_str());
+    size_t length = str.length() + 1;
+    out->insert(out->end(), data, data + length);
+    DCHECK_EQ(0u, out->back());
+  }
+}
+
+static inline void DecodeStringVector(const uint8_t** in,
+                                      const uint8_t* end,
+                                      std::vector<std::string>* strings) {
+  DCHECK(strings->empty());
+  size_t num_strings = DecodeUint32WithOverflowCheck(in, end);
+  strings->reserve(num_strings);
+  for (size_t i = 0; i < num_strings; ++i) {
+    CHECK_LT(*in, end);
+    const char* string_start = reinterpret_cast<const char*>(*in);
+    strings->emplace_back(std::string(string_start));
+    *in += strings->back().length() + 1;
+  }
+}
+
+void VerifierDeps::Encode(std::vector<uint8_t>* buffer) const {
+  MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
+  for (auto& entry : dex_deps_) {
+    EncodeStringVector(buffer, entry.second->strings_);
+    EncodeSet(buffer, entry.second->assignable_types_);
+    EncodeSet(buffer, entry.second->unassignable_types_);
+    EncodeSet(buffer, entry.second->classes_);
+    EncodeSet(buffer, entry.second->fields_);
+    EncodeSet(buffer, entry.second->direct_methods_);
+    EncodeSet(buffer, entry.second->virtual_methods_);
+    EncodeSet(buffer, entry.second->interface_methods_);
+  }
+}
+
+VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files, ArrayRef<uint8_t> data)
+    : VerifierDeps(dex_files) {
+  const uint8_t* data_start = data.data();
+  const uint8_t* data_end = data_start + data.size();
+  for (auto& entry : dex_deps_) {
+    DecodeStringVector(&data_start, data_end, &entry.second->strings_);
+    DecodeSet(&data_start, data_end, &entry.second->assignable_types_);
+    DecodeSet(&data_start, data_end, &entry.second->unassignable_types_);
+    DecodeSet(&data_start, data_end, &entry.second->classes_);
+    DecodeSet(&data_start, data_end, &entry.second->fields_);
+    DecodeSet(&data_start, data_end, &entry.second->direct_methods_);
+    DecodeSet(&data_start, data_end, &entry.second->virtual_methods_);
+    DecodeSet(&data_start, data_end, &entry.second->interface_methods_);
+  }
+  CHECK_LE(data_start, data_end);
+}
+
+bool VerifierDeps::Equals(const VerifierDeps& rhs) const {
+  MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
+
+  if (dex_deps_.size() != rhs.dex_deps_.size()) {
+    return false;
+  }
+
+  auto lhs_it = dex_deps_.begin();
+  auto rhs_it = rhs.dex_deps_.begin();
+
+  for (; (lhs_it != dex_deps_.end()) && (rhs_it != rhs.dex_deps_.end()); lhs_it++, rhs_it++) {
+    const DexFile* lhs_dex_file = lhs_it->first;
+    const DexFile* rhs_dex_file = rhs_it->first;
+    if (lhs_dex_file != rhs_dex_file) {
+      return false;
+    }
+
+    DexFileDeps* lhs_deps = lhs_it->second.get();
+    DexFileDeps* rhs_deps = rhs_it->second.get();
+    if (!lhs_deps->Equals(*rhs_deps)) {
+      return false;
+    }
+  }
+
+  DCHECK((lhs_it == dex_deps_.end()) && (rhs_it == rhs.dex_deps_.end()));
+  return true;
+}
+
+bool VerifierDeps::DexFileDeps::Equals(const VerifierDeps::DexFileDeps& rhs) const {
+  return (strings_ == rhs.strings_) &&
+         (assignable_types_ == rhs.assignable_types_) &&
+         (unassignable_types_ == rhs.unassignable_types_) &&
+         (classes_ == rhs.classes_) &&
+         (fields_ == rhs.fields_) &&
+         (direct_methods_ == rhs.direct_methods_) &&
+         (virtual_methods_ == rhs.virtual_methods_) &&
+         (interface_methods_ == rhs.interface_methods_);
+}
+
 }  // namespace verifier
 }  // namespace art
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index da63d67..dc8dfaf 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -23,6 +23,7 @@
 
 #include "art_field.h"
 #include "art_method.h"
+#include "base/array_ref.h"
 #include "base/mutex.h"
 #include "method_resolution_kind.h"
 #include "os.h"
@@ -84,14 +85,23 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::verifier_deps_lock_);
 
+  // Serialize the recorded dependencies and store the data into `buffer`.
+  void Encode(std::vector<uint8_t>* buffer) const
+      REQUIRES(!Locks::verifier_deps_lock_);
+
  private:
   static constexpr uint16_t kUnresolvedMarker = static_cast<uint16_t>(-1);
 
+  // Only used in tests to reconstruct the data structure from serialized data.
+  VerifierDeps(const std::vector<const DexFile*>& dex_files, ArrayRef<uint8_t> data)
+      REQUIRES(!Locks::verifier_deps_lock_);
+
   using ClassResolutionBase = std::tuple<uint32_t, uint16_t>;
   struct ClassResolution : public ClassResolutionBase {
+    ClassResolution() = default;
+    ClassResolution(const ClassResolution&) = default;
     ClassResolution(uint32_t type_idx, uint16_t access_flags)
         : ClassResolutionBase(type_idx, access_flags) {}
-    ClassResolution(const ClassResolution&) = default;
 
     bool IsResolved() const { return GetAccessFlags() != kUnresolvedMarker; }
     uint32_t GetDexTypeIndex() const { return std::get<0>(*this); }
@@ -100,9 +110,10 @@
 
   using FieldResolutionBase = std::tuple<uint32_t, uint16_t, uint32_t>;
   struct FieldResolution : public FieldResolutionBase {
+    FieldResolution() = default;
+    FieldResolution(const FieldResolution&) = default;
     FieldResolution(uint32_t field_idx, uint16_t access_flags, uint32_t declaring_class_idx)
         : FieldResolutionBase(field_idx, access_flags, declaring_class_idx) {}
-    FieldResolution(const FieldResolution&) = default;
 
     bool IsResolved() const { return GetAccessFlags() != kUnresolvedMarker; }
     uint32_t GetDexFieldIndex() const { return std::get<0>(*this); }
@@ -112,9 +123,10 @@
 
   using MethodResolutionBase = std::tuple<uint32_t, uint16_t, uint32_t>;
   struct MethodResolution : public MethodResolutionBase {
+    MethodResolution() = default;
+    MethodResolution(const MethodResolution&) = default;
     MethodResolution(uint32_t method_idx, uint16_t access_flags, uint32_t declaring_class_idx)
         : MethodResolutionBase(method_idx, access_flags, declaring_class_idx) {}
-    MethodResolution(const MethodResolution&) = default;
 
     bool IsResolved() const { return GetAccessFlags() != kUnresolvedMarker; }
     uint32_t GetDexMethodIndex() const { return std::get<0>(*this); }
@@ -124,9 +136,10 @@
 
   using TypeAssignabilityBase = std::tuple<uint32_t, uint32_t>;
   struct TypeAssignability : public std::tuple<uint32_t, uint32_t> {
+    TypeAssignability() = default;
+    TypeAssignability(const TypeAssignability&) = default;
     TypeAssignability(uint32_t destination_idx, uint32_t source_idx)
         : TypeAssignabilityBase(destination_idx, source_idx) {}
-    TypeAssignability(const TypeAssignability&) = default;
 
     uint32_t GetDestination() const { return std::get<0>(*this); }
     uint32_t GetSource() const { return std::get<1>(*this); }
@@ -150,6 +163,8 @@
     std::set<MethodResolution> direct_methods_;
     std::set<MethodResolution> virtual_methods_;
     std::set<MethodResolution> interface_methods_;
+
+    bool Equals(const DexFileDeps& rhs) const;
   };
 
   // Finds the DexFileDep instance associated with `dex_file`, or nullptr if
@@ -215,12 +230,16 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::verifier_deps_lock_);
 
+  bool Equals(const VerifierDeps& rhs) const
+      REQUIRES(!Locks::verifier_deps_lock_);
+
   // Map from DexFiles into dependencies collected from verification of their methods.
   std::map<const DexFile*, std::unique_ptr<DexFileDeps>> dex_deps_
       GUARDED_BY(Locks::verifier_deps_lock_);
 
   friend class VerifierDepsTest;
   ART_FRIEND_TEST(VerifierDepsTest, StringToId);
+  ART_FRIEND_TEST(VerifierDepsTest, EncodeDecode);
 };
 
 }  // namespace verifier
diff --git a/runtime/verifier/verifier_deps_test.cc b/runtime/verifier/verifier_deps_test.cc
index 41a9ad3..bbaf59f 100644
--- a/runtime/verifier/verifier_deps_test.cc
+++ b/runtime/verifier/verifier_deps_test.cc
@@ -59,11 +59,21 @@
     StackHandleScope<1> hs(Thread::Current());
     Handle<mirror::ClassLoader> class_loader_handle(
         hs.NewHandle(soa->Decode<mirror::ClassLoader*>(class_loader_)));
-    mirror::Class* result = class_linker_->FindClass(Thread::Current(),
-                                                     name.c_str(),
-                                                     class_loader_handle);
-    DCHECK(result != nullptr) << name;
-    return result;
+    mirror::Class* klass = class_linker_->FindClass(Thread::Current(),
+                                                    name.c_str(),
+                                                    class_loader_handle);
+    if (klass == nullptr) {
+      DCHECK(Thread::Current()->IsExceptionPending());
+      Thread::Current()->ClearException();
+    }
+    return klass;
+  }
+
+  void SetVerifierDeps(const std::vector<const DexFile*>& dex_files) {
+    verifier_deps_.reset(new verifier::VerifierDeps(dex_files));
+    VerifierDepsCompilerCallbacks* callbacks =
+        reinterpret_cast<VerifierDepsCompilerCallbacks*>(callbacks_.get());
+    callbacks->SetVerifierDeps(verifier_deps_.get());
   }
 
   void LoadDexFile(ScopedObjectAccess* soa) REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -72,16 +82,13 @@
     CHECK_EQ(dex_files.size(), 1u);
     dex_file_ = dex_files.front();
 
+    SetVerifierDeps(dex_files);
+
     mirror::ClassLoader* loader = soa->Decode<mirror::ClassLoader*>(class_loader_);
     class_linker_->RegisterDexFile(*dex_file_, loader);
 
     klass_Main_ = FindClassByName("LMain;", soa);
     CHECK(klass_Main_ != nullptr);
-
-    verifier_deps_.reset(new verifier::VerifierDeps(dex_files));
-    VerifierDepsCompilerCallbacks* callbacks =
-        reinterpret_cast<VerifierDepsCompilerCallbacks*>(callbacks_.get());
-    callbacks->SetVerifierDeps(verifier_deps_.get());
   }
 
   bool VerifyMethod(const std::string& method_name) {
@@ -138,15 +145,40 @@
     return !verifier.HasFailures();
   }
 
+  void VerifyDexFile() {
+    std::string error_msg;
+    ScopedObjectAccess soa(Thread::Current());
+
+    LoadDexFile(&soa);
+    SetVerifierDeps({ dex_file_ });
+
+    for (size_t i = 0; i < dex_file_->NumClassDefs(); i++) {
+      const char* descriptor = dex_file_->GetClassDescriptor(dex_file_->GetClassDef(i));
+      mirror::Class* klass = FindClassByName(descriptor, &soa);
+      if (klass != nullptr) {
+        MethodVerifier::VerifyClass(Thread::Current(),
+                                    klass,
+                                    nullptr,
+                                    true,
+                                    HardFailLogMode::kLogWarning,
+                                    &error_msg);
+      }
+    }
+  }
+
   bool TestAssignabilityRecording(const std::string& dst,
                                   const std::string& src,
                                   bool is_strict,
                                   bool is_assignable) {
     ScopedObjectAccess soa(Thread::Current());
     LoadDexFile(&soa);
+    mirror::Class* klass_dst = FindClassByName(dst, &soa);
+    DCHECK(klass_dst != nullptr);
+    mirror::Class* klass_src = FindClassByName(src, &soa);
+    DCHECK(klass_src != nullptr);
     verifier_deps_->AddAssignability(*dex_file_,
-                                     FindClassByName(dst, &soa),
-                                     FindClassByName(src, &soa),
+                                     klass_dst,
+                                     klass_src,
                                      is_strict,
                                      is_assignable);
     return true;
@@ -316,6 +348,34 @@
     return false;
   }
 
+  size_t NumberOfCompiledDexFiles() {
+    MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
+    return verifier_deps_->dex_deps_.size();
+  }
+
+  size_t HasEachKindOfRecord() {
+    MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
+
+    bool has_strings = false;
+    bool has_assignability = false;
+    bool has_classes = false;
+    bool has_fields = false;
+    bool has_methods = false;
+
+    for (auto& entry : verifier_deps_->dex_deps_) {
+      has_strings |= !entry.second->strings_.empty();
+      has_assignability |= !entry.second->assignable_types_.empty();
+      has_assignability |= !entry.second->unassignable_types_.empty();
+      has_classes |= !entry.second->classes_.empty();
+      has_fields |= !entry.second->fields_.empty();
+      has_methods |= !entry.second->direct_methods_.empty();
+      has_methods |= !entry.second->virtual_methods_.empty();
+      has_methods |= !entry.second->interface_methods_.empty();
+    }
+
+    return has_strings && has_assignability && has_classes && has_fields && has_methods;
+  }
+
   std::unique_ptr<verifier::VerifierDeps> verifier_deps_;
   const DexFile* dex_file_;
   jobject class_loader_;
@@ -982,5 +1042,19 @@
       "virtual", "Ljava/lang/Integer;", "intValue", "()I", true, "public", "Ljava/lang/Integer;"));
 }
 
+TEST_F(VerifierDepsTest, EncodeDecode) {
+  VerifyDexFile();
+
+  ASSERT_EQ(1u, NumberOfCompiledDexFiles());
+  ASSERT_TRUE(HasEachKindOfRecord());
+
+  std::vector<uint8_t> buffer;
+  verifier_deps_->Encode(&buffer);
+  ASSERT_FALSE(buffer.empty());
+
+  VerifierDeps decoded_deps({ dex_file_ }, ArrayRef<uint8_t>(buffer));
+  ASSERT_TRUE(verifier_deps_->Equals(decoded_deps));
+}
+
 }  // namespace verifier
 }  // namespace art