text: /*
   * 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.
   */
  
  #ifndef ART_RUNTIME_IMT_CONFLICT_TABLE_H_
  #define ART_RUNTIME_IMT_CONFLICT_TABLE_H_
  
  #include <cstddef>
  
  #include "base/casts.h"
  #include "base/enums.h"
  #include "base/macros.h"
  
  namespace art {
  
  class ArtMethod;
  
  // Table to resolve IMT conflicts at runtime. The table is attached to
  // the jni entrypoint of IMT conflict ArtMethods.
  // The table contains a list of pairs of { interface_method, implementation_method }
  // with the last entry being null to make an assembly implementation of a lookup
  // faster.
  class ImtConflictTable {
    enum MethodIndex {
      kMethodInterface,
      kMethodImplementation,
      kMethodCount,  // Number of elements in enum.
    };
  
   public:
    // Build a new table copying `other` and adding the new entry formed of
    // the pair { `interface_method`, `implementation_method` }
    ImtConflictTable(ImtConflictTable* other,
                     ArtMethod* interface_method,
                     ArtMethod* implementation_method,
                     PointerSize pointer_size) {
      const size_t count = other->NumEntries(pointer_size);
      for (size_t i = 0; i < count; ++i) {
        SetInterfaceMethod(i, pointer_size, other->GetInterfaceMethod(i, pointer_size));
        SetImplementationMethod(i, pointer_size, other->GetImplementationMethod(i, pointer_size));
      }
      SetInterfaceMethod(count, pointer_size, interface_method);
      SetImplementationMethod(count, pointer_size, implementation_method);
      // Add the null marker.
      SetInterfaceMethod(count + 1, pointer_size, nullptr);
      SetImplementationMethod(count + 1, pointer_size, nullptr);
    }
  
    // num_entries excludes the header.
    ImtConflictTable(size_t num_entries, PointerSize pointer_size) {
      SetInterfaceMethod(num_entries, pointer_size, nullptr);
      SetImplementationMethod(num_entries, pointer_size, nullptr);
    }
  
    // Set an entry at an index.
    void SetInterfaceMethod(size_t index, PointerSize pointer_size, ArtMethod* method) {
      SetMethod(index * kMethodCount + kMethodInterface, pointer_size, method);
    }
  
    void SetImplementationMethod(size_t index, PointerSize pointer_size, ArtMethod* method) {
      SetMethod(index * kMethodCount + kMethodImplementation, pointer_size, method);
    }
  
    ArtMethod* GetInterfaceMethod(size_t index, PointerSize pointer_size) const {
      return GetMethod(index * kMethodCount + kMethodInterface, pointer_size);
    }
  
    ArtMethod* GetImplementationMethod(size_t index, PointerSize pointer_size) const {
      return GetMethod(index * kMethodCount + kMethodImplementation, pointer_size);
    }
  
    void** AddressOfInterfaceMethod(size_t index, PointerSize pointer_size) {
      return AddressOfMethod(index * kMethodCount + kMethodInterface, pointer_size);
    }
  
    void** AddressOfImplementationMethod(size_t index, PointerSize pointer_size) {
      return AddressOfMethod(index * kMethodCount + kMethodImplementation, pointer_size);
    }
  
    // Return true if two conflict tables are the same.
    bool Equals(ImtConflictTable* other, PointerSize pointer_size) const {
      size_t num = NumEntries(pointer_size);
      if (num != other->NumEntries(pointer_size)) {
        return false;
      }
      for (size_t i = 0; i < num; ++i) {
        if (GetInterfaceMethod(i, pointer_size) != other->GetInterfaceMethod(i, pointer_size) ||
            GetImplementationMethod(i, pointer_size) !=
                other->GetImplementationMethod(i, pointer_size)) {
          return false;
        }
      }
      return true;
    }
  
    // Visit all of the entries.
    // NO_THREAD_SAFETY_ANALYSIS for calling with held locks. Visitor is passed a pair of ArtMethod*
    // and also returns one. The order is <interface, implementation>.
    template<typename Visitor>
    void Visit(const Visitor& visitor, PointerSize pointer_size) NO_THREAD_SAFETY_ANALYSIS {
      uint32_t table_index = 0;
      for (;;) {
        ArtMethod* interface_method = GetInterfaceMethod(table_index, pointer_size);
        if (interface_method == nullptr) {
          break;
        }
        ArtMethod* implementation_method = GetImplementationMethod(table_index, pointer_size);
        auto input = std::make_pair(interface_method, implementation_method);
        std::pair<ArtMethod*, ArtMethod*> updated = visitor(input);
        if (input.first != updated.first) {
          SetInterfaceMethod(table_index, pointer_size, updated.first);
        }
        if (input.second != updated.second) {
          SetImplementationMethod(table_index, pointer_size, updated.second);
        }
        ++table_index;
      }
    }
  
    // Lookup the implementation ArtMethod associated to `interface_method`. Return null
    // if not found.
    ArtMethod* Lookup(ArtMethod* interface_method, PointerSize pointer_size) const {
      uint32_t table_index = 0;
      for (;;) {
        ArtMethod* current_interface_method = GetInterfaceMethod(table_index, pointer_size);
        if (current_interface_method == nullptr) {
          break;
        }
        if (current_interface_method == interface_method) {
          return GetImplementationMethod(table_index, pointer_size);
        }
        ++table_index;
      }
      return nullptr;
    }
  
    // Compute the number of entries in this table.
    size_t NumEntries(PointerSize pointer_size) const {
      uint32_t table_index = 0;
      while (GetInterfaceMethod(table_index, pointer_size) != nullptr) {
        ++table_index;
      }
      return table_index;
    }
  
    // Compute the size in bytes taken by this table.
    size_t ComputeSize(PointerSize pointer_size) const {
      // Add the end marker.
      return ComputeSize(NumEntries(pointer_size), pointer_size);
    }
  
    // Compute the size in bytes needed for copying the given `table` and add
    // one more entry.
    static size_t ComputeSizeWithOneMoreEntry(ImtConflictTable* table, PointerSize pointer_size) {
      return table->ComputeSize(pointer_size) + EntrySize(pointer_size);
    }
  
    // Compute size with a fixed number of entries.
    static size_t ComputeSize(size_t num_entries, PointerSize pointer_size) {
      return (num_entries + 1) * EntrySize(pointer_size);  // Add one for null terminator.
    }
  
    static size_t EntrySize(PointerSize pointer_size) {
      return static_cast<size_t>(pointer_size) * static_cast<size_t>(kMethodCount);
    }
  
   private:
    void** AddressOfMethod(size_t index, PointerSize pointer_size) {
      if (pointer_size == PointerSize::k64) {
        return reinterpret_cast<void**>(&data64_[index]);
      } else {
        return reinterpret_cast<void**>(&data32_[index]);
      }
    }
  
    ArtMethod* GetMethod(size_t index, PointerSize pointer_size) const {
      if (pointer_size == PointerSize::k64) {
        return reinterpret_cast<ArtMethod*>(static_cast<uintptr_t>(data64_[index]));
      } else {
        return reinterpret_cast<ArtMethod*>(static_cast<uintptr_t>(data32_[index]));
      }
    }
  
    void SetMethod(size_t index, PointerSize pointer_size, ArtMethod* method) {
      if (pointer_size == PointerSize::k64) {
        data64_[index] = dchecked_integral_cast<uint64_t>(reinterpret_cast<uintptr_t>(method));
      } else {
        data32_[index] = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(method));
      }
    }
  
    // Array of entries that the assembly stubs will iterate over. Note that this is
    // not fixed size, and we allocate data prior to calling the constructor
    // of ImtConflictTable.
    union {
      uint32_t data32_[0];
      uint64_t data64_[0];
    };
  
    DISALLOW_COPY_AND_ASSIGN(ImtConflictTable);
  };
  
  }  // namespace art
  
  #endif  // ART_RUNTIME_IMT_CONFLICT_TABLE_H_
  