/* * 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 #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 . template 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 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(pointer_size) * static_cast(kMethodCount); } private: void** AddressOfMethod(size_t index, PointerSize pointer_size) { if (pointer_size == PointerSize::k64) { return reinterpret_cast(&data64_[index]); } else { return reinterpret_cast(&data32_[index]); } } ArtMethod* GetMethod(size_t index, PointerSize pointer_size) const { if (pointer_size == PointerSize::k64) { return reinterpret_cast(static_cast(data64_[index])); } else { return reinterpret_cast(static_cast(data32_[index])); } } void SetMethod(size_t index, PointerSize pointer_size, ArtMethod* method) { if (pointer_size == PointerSize::k64) { data64_[index] = dchecked_integral_cast(reinterpret_cast(method)); } else { data32_[index] = dchecked_integral_cast(reinterpret_cast(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_