// Copyright 2021 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/objects/swiss-name-dictionary-inl.h" #include "test/cctest/cctest.h" #include "test/cctest/test-swiss-name-dictionary-infra.h" #include "test/cctest/test-swiss-name-dictionary-shared-tests.h" namespace v8 { namespace internal { namespace test_swiss_hash_table { // Executes tests by executing C++ versions of dictionary operations. class RuntimeTestRunner { public: RuntimeTestRunner(Isolate* isolate, int initial_capacity, KeyCache& keys) : isolate_{isolate}, keys_{keys} { table = isolate->factory()->NewSwissNameDictionaryWithCapacity( initial_capacity, AllocationType::kYoung); } // The runtime implementations does not depend on the CPU features and // therefore always work. static bool IsEnabled() { return true; } void Add(Handle key, Handle value, PropertyDetails details); InternalIndex FindEntry(Handle key); // Updates the value and property details of the given entry. void Put(InternalIndex entry, Handle new_value, PropertyDetails new_details); void Delete(InternalIndex entry); void RehashInplace(); void Shrink(); // Retrieves data associated with |entry|, which must be an index pointing to // an existing entry. The returned array contains key, value, property details // in that order. Handle GetData(InternalIndex entry); // Tests that the current table has the given capacity, and number of // (deleted) elements, based on which optional values are present. void CheckCounts(base::Optional capacity, base::Optional elements, base::Optional deleted); // Checks that |expected_keys| contains exactly the keys in the current table, // in the given order. void CheckEnumerationOrder(const std::vector& expected_keys); void CheckCopy(); void VerifyHeap(); // Just for debugging. void PrintTable(); Handle table; private: Isolate* isolate_; KeyCache& keys_; }; void RuntimeTestRunner::Add(Handle key, Handle value, PropertyDetails details) { Handle updated_table = SwissNameDictionary::Add(isolate_, this->table, key, value, details); this->table = updated_table; } InternalIndex RuntimeTestRunner::FindEntry(Handle key) { return table->FindEntry(isolate_, key); } Handle RuntimeTestRunner::GetData(InternalIndex entry) { if (entry.is_found()) { Handle data = isolate_->factory()->NewFixedArray(3); data->set(0, table->KeyAt(entry)); data->set(1, table->ValueAt(entry)); data->set(2, table->DetailsAt(entry).AsSmi()); return data; } else { return handle(ReadOnlyRoots(isolate_).empty_fixed_array(), isolate_); } } void RuntimeTestRunner::Put(InternalIndex entry, Handle new_value, PropertyDetails new_details) { CHECK(entry.is_found()); table->ValueAtPut(entry, *new_value); table->DetailsAtPut(entry, new_details); } void RuntimeTestRunner::Delete(InternalIndex entry) { CHECK(entry.is_found()); table = table->DeleteEntry(isolate_, table, entry); } void RuntimeTestRunner::CheckCounts(base::Optional capacity, base::Optional elements, base::Optional deleted) { if (capacity.has_value()) { CHECK_EQ(capacity.value(), table->Capacity()); } if (elements.has_value()) { CHECK_EQ(elements.value(), table->NumberOfElements()); } if (deleted.has_value()) { CHECK_EQ(deleted.value(), table->NumberOfDeletedElements()); } } void RuntimeTestRunner::CheckEnumerationOrder( const std::vector& expected_keys) { ReadOnlyRoots roots(isolate_); int i = 0; for (InternalIndex index : table->IterateEntriesOrdered()) { Object key; if (table->ToKey(roots, index, &key)) { CHECK_LT(i, expected_keys.size()); Handle expected_key = CreateKeyWithHash(isolate_, this->keys_, Key{expected_keys[i]}); CHECK_EQ(key, *expected_key); ++i; } } CHECK_EQ(i, expected_keys.size()); } void RuntimeTestRunner::RehashInplace() { table->Rehash(isolate_); } void RuntimeTestRunner::Shrink() { table = SwissNameDictionary::Shrink(isolate_, table); } void RuntimeTestRunner::CheckCopy() { Handle copy = SwissNameDictionary::ShallowCopy(isolate_, table); CHECK(table->EqualsForTesting(*copy)); } void RuntimeTestRunner::VerifyHeap() { #if VERIFY_HEAP table->SwissNameDictionaryVerify(isolate_, true); #endif } void RuntimeTestRunner::PrintTable() { #ifdef OBJECT_PRINT table->SwissNameDictionaryPrint(std::cout); #endif } TEST(CapacityFor) { for (int elements = 0; elements <= 32; elements++) { int capacity = SwissNameDictionary::CapacityFor(elements); if (elements == 0) { CHECK_EQ(0, capacity); } else if (elements <= 3) { CHECK_EQ(4, capacity); } else if (elements == 4) { CHECK_IMPLIES(SwissNameDictionary::kGroupWidth == 8, capacity == 8); CHECK_IMPLIES(SwissNameDictionary::kGroupWidth == 16, capacity == 4); } else if (elements <= 7) { CHECK_EQ(8, capacity); } else if (elements <= 14) { CHECK_EQ(16, capacity); } else if (elements <= 28) { CHECK_EQ(32, capacity); } else if (elements <= 32) { CHECK_EQ(64, capacity); } } } TEST(MaxUsableCapacity) { CHECK_EQ(0, SwissNameDictionary::MaxUsableCapacity(0)); CHECK_IMPLIES(SwissNameDictionary::kGroupWidth == 8, SwissNameDictionary::MaxUsableCapacity(4) == 3); CHECK_IMPLIES(SwissNameDictionary::kGroupWidth == 16, SwissNameDictionary::MaxUsableCapacity(4) == 4); CHECK_EQ(7, SwissNameDictionary::MaxUsableCapacity(8)); CHECK_EQ(14, SwissNameDictionary::MaxUsableCapacity(16)); CHECK_EQ(28, SwissNameDictionary::MaxUsableCapacity(32)); } TEST(SizeFor) { int baseline = HeapObject::kHeaderSize + // prefix: 4 + // capacity: 4 + // meta table: kTaggedSize; int size_0 = baseline + // ctrl table: SwissNameDictionary::kGroupWidth; int size_4 = baseline + // data table: 4 * 2 * kTaggedSize + // ctrl table: 4 + SwissNameDictionary::kGroupWidth + // property details table: 4; int size_8 = baseline + // data table: 8 * 2 * kTaggedSize + // ctrl table: 8 + SwissNameDictionary::kGroupWidth + // property details table: 8; CHECK_EQ(SwissNameDictionary::SizeFor(0), size_0); CHECK_EQ(SwissNameDictionary::SizeFor(4), size_4); CHECK_EQ(SwissNameDictionary::SizeFor(8), size_8); } // Executes the tests defined in test-swiss-name-dictionary-shared-tests.h as if // they were defined in this file, using the RuntimeTestRunner. See comments in // test-swiss-name-dictionary-shared-tests.h and in // swiss-name-dictionary-infra.h for details. const char kRuntimeTestFileName[] = __FILE__; SharedSwissTableTests execute_shared_tests_runtime; } // namespace test_swiss_hash_table } // namespace internal } // namespace v8