Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

View File

@ -0,0 +1,183 @@
// Copyright 2017 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/utils/allocation.h"
#include "test/unittests/test-utils.h"
#if V8_OS_POSIX
#include <setjmp.h>
#include <signal.h>
#include <unistd.h>
#endif // V8_OS_POSIX
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace internal {
// TODO(eholk): Add a windows version of permissions tests.
#if V8_OS_POSIX
namespace {
// These tests make sure the routines to allocate memory do so with the correct
// permissions.
//
// Unfortunately, there is no API to find the protection of a memory address,
// so instead we test permissions by installing a signal handler, probing a
// memory location and recovering from the fault.
//
// We don't test the execution permission because to do so we'd have to
// dynamically generate code and test if we can execute it.
class MemoryAllocationPermissionsTest : public TestWithPlatform {
static void SignalHandler(int signal, siginfo_t* info, void*) {
#if V8_HAS_PKU_JIT_WRITE_PROTECT
RwxMemoryWriteScope::SetDefaultPermissionsForSignalHandler();
#endif
siglongjmp(continuation_, 1);
}
struct sigaction old_action_;
// On Mac, sometimes we get SIGBUS instead of SIGSEGV.
#if V8_OS_DARWIN
struct sigaction old_bus_action_;
#endif
protected:
void SetUp() override {
struct sigaction action;
action.sa_sigaction = SignalHandler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &action, &old_action_);
#if V8_OS_DARWIN
sigaction(SIGBUS, &action, &old_bus_action_);
#endif
}
void TearDown() override {
// Be a good citizen and restore the old signal handler.
sigaction(SIGSEGV, &old_action_, nullptr);
#if V8_OS_DARWIN
sigaction(SIGBUS, &old_bus_action_, nullptr);
#endif
}
public:
static sigjmp_buf continuation_;
enum class MemoryAction { kRead, kWrite };
void ProbeMemory(volatile int* buffer, MemoryAction action,
bool should_succeed) {
const int save_sigs = 1;
if (!sigsetjmp(continuation_, save_sigs)) {
switch (action) {
case MemoryAction::kRead: {
// static_cast to remove the reference and force a memory read.
USE(static_cast<int>(*buffer));
break;
}
case MemoryAction::kWrite: {
*buffer = 0;
break;
}
}
if (should_succeed) {
SUCCEED();
} else {
FAIL();
}
return;
}
if (should_succeed) {
FAIL();
} else {
SUCCEED();
}
}
void TestPermissions(PageAllocator::Permission permission, bool can_read,
bool can_write) {
v8::PageAllocator* page_allocator =
v8::internal::GetPlatformPageAllocator();
const size_t page_size = page_allocator->AllocatePageSize();
int* buffer = static_cast<int*>(AllocatePages(
page_allocator, nullptr, page_size, page_size, permission));
ProbeMemory(buffer, MemoryAction::kRead, can_read);
ProbeMemory(buffer, MemoryAction::kWrite, can_write);
FreePages(page_allocator, buffer, page_size);
}
};
sigjmp_buf MemoryAllocationPermissionsTest::continuation_;
} // namespace
// TODO(almuthanna): This test was skipped because it causes a crash when it is
// ran on Fuchsia. This issue should be solved later on
// Ticket: https://crbug.com/1028617
#if !defined(V8_TARGET_OS_FUCHSIA)
TEST_F(MemoryAllocationPermissionsTest, DoTest) {
TestPermissions(PageAllocator::Permission::kNoAccess, false, false);
TestPermissions(PageAllocator::Permission::kRead, true, false);
TestPermissions(PageAllocator::Permission::kReadWrite, true, true);
TestPermissions(PageAllocator::Permission::kReadWriteExecute, true, true);
TestPermissions(PageAllocator::Permission::kReadExecute, true, false);
}
#endif
#endif // V8_OS_POSIX
// Basic tests of allocation.
class AllocationTest : public TestWithPlatform {};
TEST_F(AllocationTest, AllocateAndFree) {
size_t page_size = v8::internal::AllocatePageSize();
CHECK_NE(0, page_size);
v8::PageAllocator* page_allocator = v8::internal::GetPlatformPageAllocator();
// A large allocation, aligned at native allocation granularity.
const size_t kAllocationSize = 1 * v8::internal::MB;
void* mem_addr = v8::internal::AllocatePages(
page_allocator, page_allocator->GetRandomMmapAddr(), kAllocationSize,
page_size, PageAllocator::Permission::kReadWrite);
CHECK_NOT_NULL(mem_addr);
v8::internal::FreePages(page_allocator, mem_addr, kAllocationSize);
// A large allocation, aligned significantly beyond native granularity.
const size_t kBigAlignment = 64 * v8::internal::MB;
void* aligned_mem_addr = v8::internal::AllocatePages(
page_allocator,
AlignedAddress(page_allocator->GetRandomMmapAddr(), kBigAlignment),
kAllocationSize, kBigAlignment, PageAllocator::Permission::kReadWrite);
CHECK_NOT_NULL(aligned_mem_addr);
CHECK_EQ(aligned_mem_addr, AlignedAddress(aligned_mem_addr, kBigAlignment));
v8::internal::FreePages(page_allocator, aligned_mem_addr, kAllocationSize);
}
TEST_F(AllocationTest, ReserveMemory) {
v8::PageAllocator* page_allocator = v8::internal::GetPlatformPageAllocator();
size_t page_size = v8::internal::AllocatePageSize();
const size_t kAllocationSize = 1 * v8::internal::MB;
void* mem_addr = v8::internal::AllocatePages(
page_allocator, page_allocator->GetRandomMmapAddr(), kAllocationSize,
page_size, PageAllocator::Permission::kReadWrite);
CHECK_NE(0, page_size);
CHECK_NOT_NULL(mem_addr);
size_t commit_size = page_allocator->CommitPageSize();
CHECK(v8::internal::SetPermissions(page_allocator, mem_addr, commit_size,
PageAllocator::Permission::kReadWrite));
// Check whether we can write to memory.
int* addr = static_cast<int*>(mem_addr);
addr[v8::internal::KB - 1] = 2;
CHECK(v8::internal::SetPermissions(page_allocator, mem_addr, commit_size,
PageAllocator::Permission::kNoAccess));
v8::internal::FreePages(page_allocator, mem_addr, kAllocationSize);
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,268 @@
// Copyright 2022 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/utils/bit-vector.h"
#include <stdlib.h>
#include "src/init/v8.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace internal {
using BitVectorTest = TestWithZone;
TEST_F(BitVectorTest, SmallBitVector) {
BitVector v(15, zone());
v.Add(1);
EXPECT_TRUE(v.Contains(1));
v.Remove(0);
EXPECT_FALSE(v.Contains(0));
v.Add(0);
v.Add(1);
BitVector w(15, zone());
w.Add(1);
v.Intersect(w);
EXPECT_FALSE(v.Contains(0));
EXPECT_TRUE(v.Contains(1));
}
TEST_F(BitVectorTest, SmallBitVectorIterator) {
BitVector v(64, zone());
v.Add(27);
v.Add(30);
v.Add(31);
v.Add(33);
BitVector::Iterator iter = v.begin();
BitVector::Iterator end = v.end();
EXPECT_NE(iter, end);
EXPECT_EQ(27, *iter);
++iter;
EXPECT_NE(iter, end);
EXPECT_EQ(30, *iter);
++iter;
EXPECT_NE(iter, end);
EXPECT_EQ(31, *iter);
++iter;
EXPECT_NE(iter, end);
EXPECT_EQ(33, *iter);
++iter;
EXPECT_TRUE(iter == end);
EXPECT_FALSE(iter != end);
}
TEST_F(BitVectorTest, Union) {
BitVector v(15, zone());
v.Add(0);
BitVector w(15, zone());
w.Add(1);
v.Union(w);
EXPECT_TRUE(v.Contains(0));
EXPECT_TRUE(v.Contains(1));
}
TEST_F(BitVectorTest, CopyFrom) {
BitVector v(15, zone());
v.Add(0);
BitVector w(15, zone());
w.CopyFrom(v);
EXPECT_TRUE(w.Contains(0));
w.Add(1);
BitVector u(w, zone());
EXPECT_TRUE(u.Contains(0));
EXPECT_TRUE(u.Contains(1));
v.Union(w);
EXPECT_TRUE(v.Contains(0));
EXPECT_TRUE(v.Contains(1));
}
TEST_F(BitVectorTest, Union2) {
BitVector v(35, zone());
v.Add(0);
BitVector w(35, zone());
w.Add(33);
v.Union(w);
EXPECT_TRUE(v.Contains(0));
EXPECT_TRUE(v.Contains(33));
}
TEST_F(BitVectorTest, Intersect) {
BitVector v(35, zone());
v.Add(32);
v.Add(33);
BitVector w(35, zone());
w.Add(33);
v.Intersect(w);
EXPECT_FALSE(v.Contains(32));
EXPECT_TRUE(v.Contains(33));
BitVector r(35, zone());
r.CopyFrom(v);
EXPECT_FALSE(r.Contains(32));
EXPECT_TRUE(r.Contains(33));
}
TEST_F(BitVectorTest, Resize) {
BitVector v(35, zone());
v.Add(32);
v.Add(33);
EXPECT_TRUE(v.Contains(32));
EXPECT_TRUE(v.Contains(33));
EXPECT_FALSE(v.Contains(22));
EXPECT_FALSE(v.Contains(34));
v.Resize(50, zone());
EXPECT_TRUE(v.Contains(32));
EXPECT_TRUE(v.Contains(33));
EXPECT_FALSE(v.Contains(22));
EXPECT_FALSE(v.Contains(34));
EXPECT_FALSE(v.Contains(43));
v.Resize(300, zone());
EXPECT_TRUE(v.Contains(32));
EXPECT_TRUE(v.Contains(33));
EXPECT_FALSE(v.Contains(22));
EXPECT_FALSE(v.Contains(34));
EXPECT_FALSE(v.Contains(43));
EXPECT_FALSE(v.Contains(243));
}
TEST_F(BitVectorTest, BigBitVectorIterator) {
// Big BitVector with big and small entries.
BitVector v(500, zone());
v.Add(27);
v.Add(300);
v.Add(499);
auto iter = v.begin();
auto end = v.end();
EXPECT_NE(iter, end);
EXPECT_EQ(27, *iter);
++iter;
EXPECT_NE(iter, end);
EXPECT_EQ(300, *iter);
++iter;
EXPECT_NE(iter, end);
EXPECT_EQ(499, *iter);
++iter;
EXPECT_EQ(iter, end);
// Remove small entries, add another big one.
v.Resize(1000, zone());
v.Remove(27);
v.Remove(300);
v.Add(500);
iter = v.begin();
end = v.end();
EXPECT_NE(iter, end);
EXPECT_EQ(499, *iter);
++iter;
EXPECT_NE(iter, end);
EXPECT_EQ(500, *iter);
++iter;
EXPECT_EQ(iter, end);
}
TEST_F(BitVectorTest, MoveConstructorInline) {
BitVector v(30, zone());
v.Add(12);
v.Add(29);
EXPECT_TRUE(v.Contains(12));
EXPECT_TRUE(v.Contains(29));
EXPECT_FALSE(v.Contains(22));
EXPECT_FALSE(v.Contains(28));
BitVector a(std::move(v));
EXPECT_TRUE(a.Contains(12));
EXPECT_TRUE(a.Contains(29));
EXPECT_FALSE(a.Contains(22));
EXPECT_FALSE(a.Contains(28));
// Check the data from `v` was properly moved out and doesn't affect `a`.
// As moving out doesn't provide a clear state of the moved out object,
// explicitly set it to a well-known state.
v = BitVector(31, zone());
v.Add(22);
v.Add(28);
EXPECT_TRUE(a.Contains(12));
EXPECT_TRUE(a.Contains(29));
EXPECT_FALSE(a.Contains(22));
EXPECT_FALSE(a.Contains(28));
}
TEST_F(BitVectorTest, MoveAssignInline) {
BitVector v(30, zone());
v.Add(12);
v.Add(29);
EXPECT_TRUE(v.Contains(12));
EXPECT_TRUE(v.Contains(29));
EXPECT_FALSE(v.Contains(22));
EXPECT_FALSE(v.Contains(28));
BitVector a;
a = std::move(v);
EXPECT_TRUE(a.Contains(12));
EXPECT_TRUE(a.Contains(29));
EXPECT_FALSE(a.Contains(22));
EXPECT_FALSE(a.Contains(28));
// Check the data from `v` was properly moved out and doesn't affect `a`.
// As moving out doesn't provide a clear state of the moved out object,
// explicitly set it to a well-known state.
v = BitVector(31, zone());
v.Add(22);
v.Add(28);
EXPECT_TRUE(a.Contains(12));
EXPECT_TRUE(a.Contains(29));
EXPECT_FALSE(a.Contains(22));
EXPECT_FALSE(a.Contains(28));
}
TEST_F(BitVectorTest, MoveConstructorLarge) {
BitVector v(200, zone());
v.Add(31);
v.Add(133);
EXPECT_TRUE(v.Contains(31));
EXPECT_TRUE(v.Contains(133));
EXPECT_FALSE(v.Contains(22));
EXPECT_FALSE(v.Contains(134));
BitVector a(std::move(v));
EXPECT_TRUE(a.Contains(31));
EXPECT_TRUE(a.Contains(133));
EXPECT_FALSE(a.Contains(22));
EXPECT_FALSE(a.Contains(134));
// Check the data from `v` was properly moved out and doesn't affect `a`.
// As moving out doesn't provide a clear state of the moved out object,
// explicitly set it to a well-known state.
v = BitVector(205, zone());
v.Add(22);
v.Add(134);
EXPECT_TRUE(a.Contains(31));
EXPECT_TRUE(a.Contains(133));
EXPECT_FALSE(a.Contains(22));
EXPECT_FALSE(a.Contains(134));
}
TEST_F(BitVectorTest, MoveAssignLarge) {
BitVector v(200, zone());
v.Add(31);
v.Add(133);
EXPECT_TRUE(v.Contains(31));
EXPECT_TRUE(v.Contains(133));
EXPECT_FALSE(v.Contains(22));
EXPECT_FALSE(v.Contains(134));
BitVector a;
a = std::move(v);
EXPECT_TRUE(a.Contains(31));
EXPECT_TRUE(a.Contains(133));
EXPECT_FALSE(a.Contains(22));
EXPECT_FALSE(a.Contains(134));
// Check the data from `v` was properly moved out and doesn't affect `a`.
// As moving out doesn't provide a clear state of the moved out object,
// explicitly set it to a well-known state.
v = BitVector(205, zone());
v.Add(22);
v.Add(134);
EXPECT_TRUE(a.Contains(31));
EXPECT_TRUE(a.Contains(133));
EXPECT_FALSE(a.Contains(22));
EXPECT_FALSE(a.Contains(134));
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,127 @@
// Copyright 2017 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/utils/detachable-vector.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace internal {
TEST(DetachableVector, ConstructIsEmpty) {
DetachableVector<int> v;
size_t empty_size = 0;
EXPECT_EQ(empty_size, v.size());
EXPECT_TRUE(v.empty());
}
TEST(DetachableVector, PushAddsElement) {
DetachableVector<int> v;
v.push_back(1);
EXPECT_EQ(1, v.front());
EXPECT_EQ(1, v.back());
EXPECT_EQ(1, v.at(0));
size_t one_size = 1;
EXPECT_EQ(one_size, v.size());
EXPECT_FALSE(v.empty());
}
TEST(DetachableVector, AfterFreeIsEmpty) {
DetachableVector<int> v;
v.push_back(1);
v.free();
size_t empty_size = 0;
EXPECT_EQ(empty_size, v.size());
EXPECT_TRUE(v.empty());
}
// This test relies on ASAN to detect leaks and double-frees.
TEST(DetachableVector, DetachLeaksBackingStore) {
DetachableVector<int> v;
DetachableVector<int> v2;
size_t one_size = 1;
EXPECT_TRUE(v2.empty());
// Force allocation of the backing store.
v.push_back(1);
// Bit-copy the data structure.
memcpy(&v2, &v, sizeof(DetachableVector<int>));
// The backing store should be leaked here - free was not called.
v.detach();
// We have transferred the backing store to the second vector.
EXPECT_EQ(one_size, v2.size());
EXPECT_TRUE(v.empty());
// The destructor of v2 will release the backing store.
}
TEST(DetachableVector, PushAndPopWithReallocation) {
DetachableVector<size_t> v;
const size_t kMinimumCapacity = DetachableVector<size_t>::kMinimumCapacity;
EXPECT_EQ(0u, v.capacity());
EXPECT_EQ(0u, v.size());
v.push_back(0);
EXPECT_EQ(kMinimumCapacity, v.capacity());
EXPECT_EQ(1u, v.size());
// Push values until the reallocation happens.
for (size_t i = 1; i <= kMinimumCapacity; ++i) {
v.push_back(i);
}
EXPECT_EQ(2 * kMinimumCapacity, v.capacity());
EXPECT_EQ(kMinimumCapacity + 1, v.size());
EXPECT_EQ(kMinimumCapacity, v.back());
v.pop_back();
v.push_back(100);
EXPECT_EQ(100u, v.back());
v.pop_back();
EXPECT_EQ(kMinimumCapacity - 1, v.back());
}
TEST(DetachableVector, ShrinkToFit) {
DetachableVector<size_t> v;
const size_t kMinimumCapacity = DetachableVector<size_t>::kMinimumCapacity;
// shrink_to_fit doesn't affect the empty capacity DetachableVector.
EXPECT_EQ(0u, v.capacity());
v.shrink_to_fit();
EXPECT_EQ(0u, v.capacity());
// Do not shrink the buffer if it's smaller than kMinimumCapacity.
v.push_back(0);
EXPECT_EQ(kMinimumCapacity, v.capacity());
v.shrink_to_fit();
EXPECT_EQ(kMinimumCapacity, v.capacity());
// Fill items to |v| until the buffer grows twice.
for (size_t i = 0; i < 2 * kMinimumCapacity; ++i) {
v.push_back(i);
}
EXPECT_EQ(2 * kMinimumCapacity + 1, v.size());
EXPECT_EQ(4 * kMinimumCapacity, v.capacity());
// Do not shrink the buffer if the number of unused slots is not large enough.
v.shrink_to_fit();
EXPECT_EQ(2 * kMinimumCapacity + 1, v.size());
EXPECT_EQ(4 * kMinimumCapacity, v.capacity());
v.pop_back();
v.pop_back();
v.shrink_to_fit();
EXPECT_EQ(2 * kMinimumCapacity - 1, v.size());
EXPECT_EQ(2 * kMinimumCapacity - 1, v.capacity());
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,769 @@
// Copyright 2015 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/utils/identity-map.h"
#include <set>
#include "src/execution/isolate.h"
#include "src/heap/factory-inl.h"
#include "src/objects/heap-number-inl.h"
#include "src/objects/objects.h"
#include "src/zone/zone.h"
#include "test/unittests/heap/heap-utils.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace internal {
// Helper for testing. A "friend" of the IdentityMapBase class, it is able to
// "move" objects to simulate GC for testing the internals of the map.
class IdentityMapTester {
public:
IdentityMap<void*, ZoneAllocationPolicy> map;
IdentityMapTester(Heap* heap, Zone* zone)
: map(heap, ZoneAllocationPolicy(zone)) {}
void TestInsertFind(DirectHandle<Object> key1, void* val1,
DirectHandle<Object> key2, void* val2) {
CHECK_NULL(map.Find(key1));
CHECK_NULL(map.Find(key2));
// Set {key1} the first time.
auto find_result = map.FindOrInsert(key1);
CHECK_NOT_NULL(find_result.entry);
CHECK(!find_result.already_exists);
*find_result.entry = val1;
for (int i = 0; i < 3; i++) { // Get and find {key1} K times.
{
auto new_find_result = map.FindOrInsert(key1);
CHECK(new_find_result.already_exists);
CHECK_EQ(find_result.entry, new_find_result.entry);
CHECK_EQ(val1, *new_find_result.entry);
CHECK_NULL(map.Find(key2));
}
{
void** nentry = map.Find(key1);
CHECK_EQ(find_result.entry, nentry);
CHECK_EQ(val1, *nentry);
CHECK_NULL(map.Find(key2));
}
}
// Set {key2} the first time.
auto find_result2 = map.FindOrInsert(key2);
CHECK_NOT_NULL(find_result2.entry);
CHECK(!find_result2.already_exists);
*find_result2.entry = val2;
for (int i = 0; i < 3; i++) { // Get and find {key1} and {key2} K times.
{
auto new_find_result = map.FindOrInsert(key2);
CHECK_EQ(find_result2.entry, new_find_result.entry);
CHECK_EQ(val2, *new_find_result.entry);
}
{
void** nentry = map.Find(key2);
CHECK_EQ(find_result2.entry, nentry);
CHECK_EQ(val2, *nentry);
}
{
void** nentry = map.Find(key1);
CHECK_EQ(val1, *nentry);
}
}
}
void TestFindDelete(DirectHandle<Object> key1, void* val1,
DirectHandle<Object> key2, void* val2) {
CHECK_NULL(map.Find(key1));
CHECK_NULL(map.Find(key2));
// Set {key1} and {key2} for the first time.
auto find_result1 = map.FindOrInsert(key1);
CHECK(!find_result1.already_exists);
CHECK_NOT_NULL(find_result1.entry);
*find_result1.entry = val1;
auto find_result2 = map.FindOrInsert(key2);
CHECK(!find_result1.already_exists);
CHECK_NOT_NULL(find_result2.entry);
*find_result2.entry = val2;
for (int i = 0; i < 3; i++) { // Find {key1} and {key2} 3 times.
{
void** nentry = map.Find(key2);
CHECK_EQ(val2, *nentry);
}
{
void** nentry = map.Find(key1);
CHECK_EQ(val1, *nentry);
}
}
// Delete {key1}
void* deleted_entry_1;
CHECK(map.Delete(key1, &deleted_entry_1));
CHECK_NOT_NULL(deleted_entry_1);
deleted_entry_1 = val1;
for (int i = 0; i < 3; i++) { // Find {key1} and not {key2} 3 times.
{
void** nentry = map.Find(key1);
CHECK_NULL(nentry);
}
{
void** nentry = map.Find(key2);
CHECK_EQ(val2, *nentry);
}
}
// Delete {key2}
void* deleted_entry_2;
CHECK(map.Delete(key2, &deleted_entry_2));
CHECK_NOT_NULL(deleted_entry_2);
deleted_entry_2 = val2;
for (int i = 0; i < 3; i++) { // Don't find {key1} and {key2} 3 times.
{
void** nentry = map.Find(key1);
CHECK_NULL(nentry);
}
{
void** nentry = map.Find(key2);
CHECK_NULL(nentry);
}
}
}
void SimulateGCByIncrementingSmisBy(int shift) {
for (int i = 0; i < map.capacity_; i++) {
Address key = map.keys_[i];
if (!Internals::HasHeapObjectTag(key)) {
map.keys_[i] =
Internals::IntegralToSmi(Internals::SmiValue(key) + shift);
}
}
map.gc_counter_ = -1;
}
void CheckFind(DirectHandle<Object> key, void* value) {
void** entry = map.Find(key);
CHECK_NOT_NULL(entry);
CHECK_EQ(value, *entry);
}
void CheckFindOrInsert(DirectHandle<Object> key, void* value) {
auto find_result = map.FindOrInsert(key);
CHECK(find_result.already_exists);
CHECK_NOT_NULL(find_result.entry);
CHECK_EQ(value, *find_result.entry);
}
void CheckDelete(DirectHandle<Object> key, void* value) {
void* entry;
CHECK(map.Delete(key, &entry));
CHECK_NOT_NULL(entry);
CHECK_EQ(value, entry);
}
void PrintMap() {
PrintF("{\n");
for (int i = 0; i < map.capacity_; i++) {
PrintF(" %3d: %p => %p\n", i, reinterpret_cast<void*>(map.keys_[i]),
reinterpret_cast<void*>(map.values_[i]));
}
PrintF("}\n");
}
void Resize() { map.Resize(map.capacity_ * 4); }
void Rehash() { map.Rehash(); }
};
class IdentityMapTest : public TestWithIsolateAndZone {
public:
DirectHandle<Smi> smi(int value) {
return DirectHandle<Smi>(Smi::FromInt(value), isolate());
}
Handle<Object> num(double value) {
return isolate()->factory()->NewNumber(value);
}
void IterateCollisionTest(int stride) {
for (int load = 15; load <= 120; load = load * 2) {
IdentityMapTester t(isolate()->heap(), zone());
{ // Add entries to the map.
HandleScope scope(isolate());
int next = 1;
for (int i = 0; i < load; i++) {
t.map.Insert(smi(next), reinterpret_cast<void*>(next));
t.CheckFind(smi(next), reinterpret_cast<void*>(next));
next = next + stride;
}
}
// Iterate through the map and check we see all elements only once.
std::set<intptr_t> seen;
{
IdentityMap<void*, ZoneAllocationPolicy>::IteratableScope it_scope(
&t.map);
for (auto it = it_scope.begin(); it != it_scope.end(); ++it) {
CHECK(seen.find(reinterpret_cast<intptr_t>(**it)) == seen.end());
seen.insert(reinterpret_cast<intptr_t>(**it));
}
}
// Check get and find on map.
{
HandleScope scope(isolate());
int next = 1;
for (int i = 0; i < load; i++) {
CHECK(seen.find(next) != seen.end());
t.CheckFind(smi(next), reinterpret_cast<void*>(next));
t.CheckFindOrInsert(smi(next), reinterpret_cast<void*>(next));
next = next + stride;
}
}
}
}
void CollisionTest(int stride, bool rehash = false, bool resize = false) {
for (int load = 15; load <= 120; load = load * 2) {
IdentityMapTester t(isolate()->heap(), zone());
{ // Add entries to the map.
HandleScope scope(isolate());
int next = 1;
for (int i = 0; i < load; i++) {
t.map.Insert(smi(next), reinterpret_cast<void*>(next));
t.CheckFind(smi(next), reinterpret_cast<void*>(next));
next = next + stride;
}
}
if (resize) t.Resize(); // Explicit resize (internal method).
if (rehash) t.Rehash(); // Explicit rehash (internal method).
{ // Check find and get.
HandleScope scope(isolate());
int next = 1;
for (int i = 0; i < load; i++) {
t.CheckFind(smi(next), reinterpret_cast<void*>(next));
t.CheckFindOrInsert(smi(next), reinterpret_cast<void*>(next));
next = next + stride;
}
}
}
}
};
TEST_F(IdentityMapTest, Find_smi_not_found) {
IdentityMapTester t(isolate()->heap(), zone());
for (int i = 0; i < 100; i++) {
CHECK_NULL(t.map.Find(smi(i)));
}
}
TEST_F(IdentityMapTest, Find_num_not_found) {
IdentityMapTester t(isolate()->heap(), zone());
for (int i = 0; i < 100; i++) {
CHECK_NULL(t.map.Find(num(i + 0.2)));
}
}
TEST_F(IdentityMapTest, Delete_smi_not_found) {
IdentityMapTester t(isolate()->heap(), zone());
for (int i = 0; i < 100; i++) {
void* deleted_value = &t;
CHECK(!t.map.Delete(smi(i), &deleted_value));
CHECK_EQ(&t, deleted_value);
}
}
TEST_F(IdentityMapTest, Delete_num_not_found) {
IdentityMapTester t(isolate()->heap(), zone());
for (int i = 0; i < 100; i++) {
void* deleted_value = &t;
CHECK(!t.map.Delete(num(i + 0.2), &deleted_value));
CHECK_EQ(&t, deleted_value);
}
}
TEST_F(IdentityMapTest, GetFind_smi_0) {
IdentityMapTester t(isolate()->heap(), zone());
t.TestInsertFind(smi(0), isolate(), smi(1), isolate()->heap());
}
TEST_F(IdentityMapTest, GetFind_smi_13) {
IdentityMapTester t(isolate()->heap(), zone());
t.TestInsertFind(smi(13), isolate(), smi(17), isolate()->heap());
}
TEST_F(IdentityMapTest, GetFind_num_13) {
IdentityMapTester t(isolate()->heap(), zone());
t.TestInsertFind(num(13.1), isolate(), num(17.1), isolate()->heap());
}
TEST_F(IdentityMapTest, Delete_smi_13) {
IdentityMapTester t(isolate()->heap(), zone());
t.TestFindDelete(smi(13), isolate(), smi(17), isolate()->heap());
CHECK(t.map.empty());
}
TEST_F(IdentityMapTest, Delete_num_13) {
IdentityMapTester t(isolate()->heap(), zone());
t.TestFindDelete(num(13.1), isolate(), num(17.1), isolate()->heap());
CHECK(t.map.empty());
}
TEST_F(IdentityMapTest, GetFind_smi_17m) {
const int kInterval = 17;
const int kShift = 1099;
IdentityMapTester t(isolate()->heap(), zone());
for (int i = 1; i < 100; i += kInterval) {
t.map.Insert(smi(i), reinterpret_cast<void*>(i + kShift));
}
for (int i = 1; i < 100; i += kInterval) {
t.CheckFind(smi(i), reinterpret_cast<void*>(i + kShift));
}
for (int i = 1; i < 100; i += kInterval) {
t.CheckFindOrInsert(smi(i), reinterpret_cast<void*>(i + kShift));
}
for (int i = 1; i < 100; i++) {
void** entry = t.map.Find(smi(i));
if ((i % kInterval) != 1) {
CHECK_NULL(entry);
} else {
CHECK_NOT_NULL(entry);
CHECK_EQ(reinterpret_cast<void*>(i + kShift), *entry);
}
}
}
TEST_F(IdentityMapTest, Delete_smi_17m) {
const int kInterval = 17;
const int kShift = 1099;
IdentityMapTester t(isolate()->heap(), zone());
for (int i = 1; i < 100; i += kInterval) {
t.map.Insert(smi(i), reinterpret_cast<void*>(i + kShift));
}
for (int i = 1; i < 100; i += kInterval) {
t.CheckFind(smi(i), reinterpret_cast<void*>(i + kShift));
}
for (int i = 1; i < 100; i += kInterval) {
t.CheckDelete(smi(i), reinterpret_cast<void*>(i + kShift));
for (int j = 1; j < 100; j += kInterval) {
auto entry = t.map.Find(smi(j));
if (j <= i) {
CHECK_NULL(entry);
} else {
CHECK_NOT_NULL(entry);
CHECK_EQ(reinterpret_cast<void*>(j + kShift), *entry);
}
}
}
}
TEST_F(IdentityMapTest, GetFind_num_1000) {
const int kPrime = 137;
IdentityMapTester t(isolate()->heap(), zone());
int val1;
int val2;
for (int i = 0; i < 1000; i++) {
t.TestInsertFind(smi(i * kPrime), &val1, smi(i * kPrime + 1), &val2);
}
}
TEST_F(IdentityMapTest, Delete_num_1000) {
const int kPrime = 137;
IdentityMapTester t(isolate()->heap(), zone());
for (int i = 0; i < 1000; i++) {
t.map.Insert(smi(i * kPrime), reinterpret_cast<void*>(i * kPrime));
}
// Delete every second value in reverse.
for (int i = 999; i >= 0; i -= 2) {
void* entry;
CHECK(t.map.Delete(smi(i * kPrime), &entry));
CHECK_EQ(reinterpret_cast<void*>(i * kPrime), entry);
}
for (int i = 0; i < 1000; i++) {
auto entry = t.map.Find(smi(i * kPrime));
if (i % 2) {
CHECK_NULL(entry);
} else {
CHECK_NOT_NULL(entry);
CHECK_EQ(reinterpret_cast<void*>(i * kPrime), *entry);
}
}
// Delete the rest.
for (int i = 0; i < 1000; i += 2) {
void* entry;
CHECK(t.map.Delete(smi(i * kPrime), &entry));
CHECK_EQ(reinterpret_cast<void*>(i * kPrime), entry);
}
for (int i = 0; i < 1000; i++) {
auto entry = t.map.Find(smi(i * kPrime));
CHECK_NULL(entry);
}
}
TEST_F(IdentityMapTest, GetFind_smi_gc) {
const int kKey = 33;
const int kShift = 1211;
IdentityMapTester t(isolate()->heap(), zone());
t.map.Insert(smi(kKey), &t);
t.SimulateGCByIncrementingSmisBy(kShift);
t.CheckFind(smi(kKey + kShift), &t);
t.CheckFindOrInsert(smi(kKey + kShift), &t);
}
TEST_F(IdentityMapTest, Delete_smi_gc) {
const int kKey = 33;
const int kShift = 1211;
IdentityMapTester t(isolate()->heap(), zone());
t.map.Insert(smi(kKey), &t);
t.SimulateGCByIncrementingSmisBy(kShift);
t.CheckDelete(smi(kKey + kShift), &t);
}
TEST_F(IdentityMapTest, GetFind_smi_gc2) {
int kKey1 = 1;
int kKey2 = 33;
const int kShift = 1211;
IdentityMapTester t(isolate()->heap(), zone());
t.map.Insert(smi(kKey1), &kKey1);
t.map.Insert(smi(kKey2), &kKey2);
t.SimulateGCByIncrementingSmisBy(kShift);
t.CheckFind(smi(kKey1 + kShift), &kKey1);
t.CheckFindOrInsert(smi(kKey1 + kShift), &kKey1);
t.CheckFind(smi(kKey2 + kShift), &kKey2);
t.CheckFindOrInsert(smi(kKey2 + kShift), &kKey2);
}
TEST_F(IdentityMapTest, Delete_smi_gc2) {
int kKey1 = 1;
int kKey2 = 33;
const int kShift = 1211;
IdentityMapTester t(isolate()->heap(), zone());
t.map.Insert(smi(kKey1), &kKey1);
t.map.Insert(smi(kKey2), &kKey2);
t.SimulateGCByIncrementingSmisBy(kShift);
t.CheckDelete(smi(kKey1 + kShift), &kKey1);
t.CheckDelete(smi(kKey2 + kShift), &kKey2);
}
TEST_F(IdentityMapTest, GetFind_smi_gc_n) {
const int kShift = 12011;
IdentityMapTester t(isolate()->heap(), zone());
int keys[12] = {1, 2, 7, 8, 15, 23,
1 + 32, 2 + 32, 7 + 32, 8 + 32, 15 + 32, 23 + 32};
// Initialize the map first.
for (size_t i = 0; i < arraysize(keys); i += 2) {
t.TestInsertFind(smi(keys[i]), &keys[i], smi(keys[i + 1]), &keys[i + 1]);
}
// Check the above initialization.
for (size_t i = 0; i < arraysize(keys); i++) {
t.CheckFind(smi(keys[i]), &keys[i]);
}
// Simulate a GC by "moving" the smis in the internal keys array.
t.SimulateGCByIncrementingSmisBy(kShift);
// Check that searching for the incremented smis finds the same values.
for (size_t i = 0; i < arraysize(keys); i++) {
t.CheckFind(smi(keys[i] + kShift), &keys[i]);
}
// Check that searching for the incremented smis gets the same values.
for (size_t i = 0; i < arraysize(keys); i++) {
t.CheckFindOrInsert(smi(keys[i] + kShift), &keys[i]);
}
}
TEST_F(IdentityMapTest, Delete_smi_gc_n) {
const int kShift = 12011;
IdentityMapTester t(isolate()->heap(), zone());
int keys[12] = {1, 2, 7, 8, 15, 23,
1 + 32, 2 + 32, 7 + 32, 8 + 32, 15 + 32, 23 + 32};
// Initialize the map first.
for (size_t i = 0; i < arraysize(keys); i++) {
t.map.Insert(smi(keys[i]), &keys[i]);
}
// Simulate a GC by "moving" the smis in the internal keys array.
t.SimulateGCByIncrementingSmisBy(kShift);
// Check that deleting for the incremented smis finds the same values.
for (size_t i = 0; i < arraysize(keys); i++) {
t.CheckDelete(smi(keys[i] + kShift), &keys[i]);
}
}
TEST_F(IdentityMapTest, GetFind_smi_num_gc_n) {
const int kShift = 12019;
IdentityMapTester t(isolate()->heap(), zone());
int smi_keys[] = {1, 2, 7, 15, 23};
Handle<Object> num_keys[] = {num(1.1), num(2.2), num(3.3), num(4.4),
num(5.5), num(6.6), num(7.7), num(8.8),
num(9.9), num(10.1)};
// Initialize the map first.
for (size_t i = 0; i < arraysize(smi_keys); i++) {
t.map.Insert(smi(smi_keys[i]), &smi_keys[i]);
}
for (size_t i = 0; i < arraysize(num_keys); i++) {
t.map.Insert(num_keys[i], &num_keys[i]);
}
// Check the above initialization.
for (size_t i = 0; i < arraysize(smi_keys); i++) {
t.CheckFind(smi(smi_keys[i]), &smi_keys[i]);
}
for (size_t i = 0; i < arraysize(num_keys); i++) {
t.CheckFind(num_keys[i], &num_keys[i]);
}
// Simulate a GC by moving SMIs.
// Ironically the SMIs "move", but the heap numbers don't!
t.SimulateGCByIncrementingSmisBy(kShift);
// Check that searching for the incremented smis finds the same values.
for (size_t i = 0; i < arraysize(smi_keys); i++) {
t.CheckFind(smi(smi_keys[i] + kShift), &smi_keys[i]);
t.CheckFindOrInsert(smi(smi_keys[i] + kShift), &smi_keys[i]);
}
// Check that searching for the numbers finds the same values.
for (size_t i = 0; i < arraysize(num_keys); i++) {
t.CheckFind(num_keys[i], &num_keys[i]);
t.CheckFindOrInsert(num_keys[i], &num_keys[i]);
}
}
TEST_F(IdentityMapTest, Delete_smi_num_gc_n) {
const int kShift = 12019;
IdentityMapTester t(isolate()->heap(), zone());
int smi_keys[] = {1, 2, 7, 15, 23};
Handle<Object> num_keys[] = {num(1.1), num(2.2), num(3.3), num(4.4),
num(5.5), num(6.6), num(7.7), num(8.8),
num(9.9), num(10.1)};
// Initialize the map first.
for (size_t i = 0; i < arraysize(smi_keys); i++) {
t.map.Insert(smi(smi_keys[i]), &smi_keys[i]);
}
for (size_t i = 0; i < arraysize(num_keys); i++) {
t.map.Insert(num_keys[i], &num_keys[i]);
}
// Simulate a GC by moving SMIs.
// Ironically the SMIs "move", but the heap numbers don't!
t.SimulateGCByIncrementingSmisBy(kShift);
// Check that deleting for the incremented smis finds the same values.
for (size_t i = 0; i < arraysize(smi_keys); i++) {
t.CheckDelete(smi(smi_keys[i] + kShift), &smi_keys[i]);
}
// Check that deleting the numbers finds the same values.
for (size_t i = 0; i < arraysize(num_keys); i++) {
t.CheckDelete(num_keys[i], &num_keys[i]);
}
}
TEST_F(IdentityMapTest, Delete_smi_resizes) {
const int kKeyCount = 1024;
const int kValueOffset = 27;
IdentityMapTester t(isolate()->heap(), zone());
// Insert one element to initialize map.
t.map.Insert(smi(0), reinterpret_cast<void*>(kValueOffset));
int initial_capacity = t.map.capacity();
CHECK_LT(initial_capacity, kKeyCount);
// Insert another kKeyCount - 1 keys.
for (int i = 1; i < kKeyCount; i++) {
t.map.Insert(smi(i), reinterpret_cast<void*>(i + kValueOffset));
}
// Check capacity increased.
CHECK_GT(t.map.capacity(), initial_capacity);
CHECK_GE(t.map.capacity(), kKeyCount);
// Delete all the keys.
for (int i = 0; i < kKeyCount; i++) {
t.CheckDelete(smi(i), reinterpret_cast<void*>(i + kValueOffset));
}
// Should resize back to initial capacity.
CHECK_EQ(t.map.capacity(), initial_capacity);
}
TEST_F(IdentityMapTest, Iterator_smi_num) {
IdentityMapTester t(isolate()->heap(), zone());
int smi_keys[] = {1, 2, 7, 15, 23};
Handle<Object> num_keys[] = {num(1.1), num(2.2), num(3.3), num(4.4),
num(5.5), num(6.6), num(7.7), num(8.8),
num(9.9), num(10.1)};
// Initialize the map.
for (size_t i = 0; i < arraysize(smi_keys); i++) {
t.map.Insert(smi(smi_keys[i]), reinterpret_cast<void*>(i));
}
for (size_t i = 0; i < arraysize(num_keys); i++) {
t.map.Insert(num_keys[i], reinterpret_cast<void*>(i + 5));
}
// Check iterator sees all values once.
std::set<intptr_t> seen;
{
IdentityMap<void*, ZoneAllocationPolicy>::IteratableScope it_scope(&t.map);
for (auto it = it_scope.begin(); it != it_scope.end(); ++it) {
CHECK(seen.find(reinterpret_cast<intptr_t>(**it)) == seen.end());
seen.insert(reinterpret_cast<intptr_t>(**it));
}
}
for (intptr_t i = 0; i < 15; i++) {
CHECK(seen.find(i) != seen.end());
}
}
TEST_F(IdentityMapTest, Iterator_smi_num_gc) {
const int kShift = 16039;
IdentityMapTester t(isolate()->heap(), zone());
int smi_keys[] = {1, 2, 7, 15, 23};
Handle<Object> num_keys[] = {num(1.1), num(2.2), num(3.3), num(4.4),
num(5.5), num(6.6), num(7.7), num(8.8),
num(9.9), num(10.1)};
// Initialize the map.
for (size_t i = 0; i < arraysize(smi_keys); i++) {
t.map.Insert(smi(smi_keys[i]), reinterpret_cast<void*>(i));
}
for (size_t i = 0; i < arraysize(num_keys); i++) {
t.map.Insert(num_keys[i], reinterpret_cast<void*>(i + 5));
}
// Simulate GC by moving the SMIs.
t.SimulateGCByIncrementingSmisBy(kShift);
// Check iterator sees all values.
std::set<intptr_t> seen;
{
IdentityMap<void*, ZoneAllocationPolicy>::IteratableScope it_scope(&t.map);
for (auto it = it_scope.begin(); it != it_scope.end(); ++it) {
CHECK(seen.find(reinterpret_cast<intptr_t>(**it)) == seen.end());
seen.insert(reinterpret_cast<intptr_t>(**it));
}
}
for (intptr_t i = 0; i < 15; i++) {
CHECK(seen.find(i) != seen.end());
}
}
TEST_F(IdentityMapTest, IterateCollisions_1) { IterateCollisionTest(1); }
TEST_F(IdentityMapTest, IterateCollisions_2) { IterateCollisionTest(2); }
TEST_F(IdentityMapTest, IterateCollisions_3) { IterateCollisionTest(3); }
TEST_F(IdentityMapTest, IterateCollisions_5) { IterateCollisionTest(5); }
TEST_F(IdentityMapTest, IterateCollisions_7) { IterateCollisionTest(7); }
TEST_F(IdentityMapTest, Collisions_1) { CollisionTest(1); }
TEST_F(IdentityMapTest, Collisions_2) { CollisionTest(2); }
TEST_F(IdentityMapTest, Collisions_3) { CollisionTest(3); }
TEST_F(IdentityMapTest, Collisions_5) { CollisionTest(5); }
TEST_F(IdentityMapTest, Collisions_7) { CollisionTest(7); }
TEST_F(IdentityMapTest, Resize) { CollisionTest(9, false, true); }
TEST_F(IdentityMapTest, Rehash) { CollisionTest(11, true, false); }
TEST_F(IdentityMapTest, ExplicitGC) {
IdentityMapTester t(isolate()->heap(), zone());
Handle<Object> num_keys[] = {num(2.1), num(2.4), num(3.3), num(4.3),
num(7.5), num(6.4), num(7.3), num(8.3),
num(8.9), num(10.4)};
// Insert some objects that should be in new space.
for (size_t i = 0; i < arraysize(num_keys); i++) {
t.map.Insert(num_keys[i], &num_keys[i]);
}
// Do an explicit, real GC.
InvokeMinorGC();
// Check that searching for the numbers finds the same values.
for (size_t i = 0; i < arraysize(num_keys); i++) {
t.CheckFind(num_keys[i], &num_keys[i]);
t.CheckFindOrInsert(num_keys[i], &num_keys[i]);
}
}
TEST_F(IdentityMapTest, GCShortCutting) {
if (v8_flags.single_generation) return;
// We don't create ThinStrings immediately when using the forwarding table.
if (v8_flags.always_use_string_forwarding_table) return;
v8_flags.shortcut_strings_with_stack = true;
v8_flags.scavenger_precise_object_pinning = false;
ManualGCScope manual_gc_scope(isolate());
IdentityMapTester t(isolate()->heap(), zone());
Factory* factory = isolate()->factory();
const int kDummyValue = 0;
for (int i = 0; i < 16; i++) {
// Insert a varying number of Smis as padding to ensure some tests straddle
// a boundary where the thin string short cutting will cause size_ to be
// greater to capacity_ if not corrected by IdentityMap
// (see crbug.com/704132).
for (int j = 0; j < i; j++) {
t.map.Insert(smi(j), reinterpret_cast<void*>(kDummyValue));
}
Handle<String> thin_string =
factory->NewStringFromAsciiChecked("thin_string");
Handle<String> internalized_string =
factory->InternalizeString(thin_string);
DCHECK(IsThinString(*thin_string));
DCHECK_NE(*thin_string, *internalized_string);
// Insert both keys into the map.
t.map.Insert(thin_string, &thin_string);
t.map.Insert(internalized_string, &internalized_string);
// Do an explicit, real GC, this should short-cut the thin string to point
// to the internalized string (this is not implemented for MinorMS).
{
// If CSS pins a this string, it will not be considered as a candidate
// for shortcutting.
DisableConservativeStackScanningScopeForTesting no_stack_scanning(
isolate()->heap());
InvokeMinorGC();
}
DCHECK_IMPLIES(!v8_flags.minor_ms && !v8_flags.optimize_for_size,
*thin_string == *internalized_string);
// Check that getting the object points to one of the handles.
void** thin_string_entry = t.map.Find(thin_string);
CHECK(*thin_string_entry == &thin_string ||
*thin_string_entry == &internalized_string);
void** internalized_string_entry = t.map.Find(internalized_string);
CHECK(*internalized_string_entry == &thin_string ||
*internalized_string_entry == &internalized_string);
// Trigger resize.
for (int j = 0; j < 16; j++) {
t.map.Insert(smi(j + 16), reinterpret_cast<void*>(kDummyValue));
}
t.map.Clear();
}
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,86 @@
// Copyright 2015 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/utils/locked-queue-inl.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
using Record = int;
} // namespace
namespace v8 {
namespace internal {
TEST(LockedQueue, ConstructorEmpty) {
LockedQueue<Record> queue;
EXPECT_TRUE(queue.IsEmpty());
}
TEST(LockedQueue, SingleRecordEnqueueDequeue) {
LockedQueue<Record> queue;
EXPECT_TRUE(queue.IsEmpty());
queue.Enqueue(1);
EXPECT_FALSE(queue.IsEmpty());
Record a = -1;
bool success = queue.Dequeue(&a);
EXPECT_TRUE(success);
EXPECT_EQ(a, 1);
EXPECT_TRUE(queue.IsEmpty());
}
TEST(LockedQueue, Peek) {
LockedQueue<Record> queue;
EXPECT_TRUE(queue.IsEmpty());
queue.Enqueue(1);
EXPECT_FALSE(queue.IsEmpty());
Record a = -1;
bool success = queue.Peek(&a);
EXPECT_TRUE(success);
EXPECT_EQ(a, 1);
EXPECT_FALSE(queue.IsEmpty());
success = queue.Dequeue(&a);
EXPECT_TRUE(success);
EXPECT_EQ(a, 1);
EXPECT_TRUE(queue.IsEmpty());
}
TEST(LockedQueue, PeekOnEmpty) {
LockedQueue<Record> queue;
EXPECT_TRUE(queue.IsEmpty());
Record a = -1;
bool success = queue.Peek(&a);
EXPECT_FALSE(success);
}
TEST(LockedQueue, MultipleRecords) {
LockedQueue<Record> queue;
EXPECT_TRUE(queue.IsEmpty());
queue.Enqueue(1);
EXPECT_FALSE(queue.IsEmpty());
for (int i = 2; i <= 5; ++i) {
queue.Enqueue(i);
EXPECT_FALSE(queue.IsEmpty());
}
Record rec = 0;
for (int i = 1; i <= 4; ++i) {
EXPECT_FALSE(queue.IsEmpty());
queue.Dequeue(&rec);
EXPECT_EQ(i, rec);
}
for (int i = 6; i <= 12; ++i) {
queue.Enqueue(i);
EXPECT_FALSE(queue.IsEmpty());
}
for (int i = 5; i <= 12; ++i) {
EXPECT_FALSE(queue.IsEmpty());
queue.Dequeue(&rec);
EXPECT_EQ(i, rec);
}
EXPECT_TRUE(queue.IsEmpty());
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,96 @@
// Copyright 2022 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/utils/sparse-bit-vector.h"
#include <vector>
#include "test/unittests/test-utils.h"
#include "testing/gmock-support.h"
#include "testing/gtest-support.h"
namespace v8::internal {
using ::testing::ElementsAre;
namespace {
class SparseBitVectorBuilder {
public:
MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(SparseBitVectorBuilder);
explicit SparseBitVectorBuilder(Zone* zone) : vector_(zone) {}
template <typename... Ts>
SparseBitVectorBuilder& Add(Ts... values) {
(vector_.Add(values), ...);
return *this;
}
template <typename... Ts>
SparseBitVectorBuilder& Remove(Ts... values) {
(vector_.Remove(values), ...);
return *this;
}
std::vector<int> ToStdVector() const {
return std::vector<int>(vector_.begin(), vector_.end());
}
SparseBitVector get() { return std::move(vector_); }
private:
SparseBitVector vector_;
};
} // namespace
class SparseBitVectorTest : public TestWithZone {
public:
SparseBitVectorBuilder B() { return SparseBitVectorBuilder{zone()}; }
template <typename... Ts>
SparseBitVector Make(Ts... values) {
return B().Add(values...).get();
}
template <typename... Ts>
std::vector<int> VectorOf(Ts... values) {
return B().Add(values...).ToStdVector();
}
};
TEST_F(SparseBitVectorTest, ConstructionAndIteration) {
EXPECT_THAT(VectorOf(0, 2, 4), ElementsAre(0, 2, 4));
EXPECT_THAT(VectorOf(2000, 8000, 6000, 10000),
ElementsAre(2000, 6000, 8000, 10000));
EXPECT_THAT(VectorOf(0, 2, 2, 0, 4, 2, 4), ElementsAre(0, 2, 4));
EXPECT_THAT(VectorOf(7, 15, 31, 63, 127, 255),
ElementsAre(7, 15, 31, 63, 127, 255));
EXPECT_THAT(VectorOf(255, 127, 63, 31, 15, 7),
ElementsAre(7, 15, 31, 63, 127, 255));
}
TEST_F(SparseBitVectorTest, Contains) {
EXPECT_TRUE(Make(0, 2, 4).Contains(0));
EXPECT_FALSE(Make(0, 2, 4).Contains(1));
EXPECT_TRUE(Make(0, 2, 4).Contains(2));
EXPECT_FALSE(Make(0, 2, 4).Contains(3));
EXPECT_TRUE(Make(0, 2, 4).Contains(4));
EXPECT_TRUE(Make(2000, 8000, 6000, 10000).Contains(6000));
}
TEST_F(SparseBitVectorTest, Remove) {
EXPECT_THAT(B().Add(0, 2, 4).Remove(0).ToStdVector(), ElementsAre(2, 4));
EXPECT_THAT(B().Add(0, 2, 4).Remove(1).ToStdVector(), ElementsAre(0, 2, 4));
EXPECT_THAT(B().Add(0, 2, 4).Remove(2).ToStdVector(), ElementsAre(0, 4));
EXPECT_THAT(B().Add(0, 2, 4).Remove(3).ToStdVector(), ElementsAre(0, 2, 4));
EXPECT_THAT(B().Add(0, 2, 4).Remove(4).ToStdVector(), ElementsAre(0, 2));
EXPECT_THAT(B().Add(2000, 8000, 6000).Remove(kMaxInt).ToStdVector(),
ElementsAre(2000, 6000, 8000));
EXPECT_THAT(B().Add(2000, 8000, 6000).Remove(8000).ToStdVector(),
ElementsAre(2000, 6000));
EXPECT_THAT(B().Add(2000, 8000, 6000).Remove(2000).ToStdVector(),
ElementsAre(6000, 8000));
}
} // namespace v8::internal

View File

@ -0,0 +1,216 @@
// Copyright 2014 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 <limits>
#include "src/base/bounds.h"
#include "src/utils/utils.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace internal {
template <typename T>
class UtilsTest : public ::testing::Test {};
using IntegerTypes =
::testing::Types<signed char, unsigned char,
short, // NOLINT(runtime/int)
unsigned short, // NOLINT(runtime/int)
int, unsigned int, long, // NOLINT(runtime/int)
unsigned long, // NOLINT(runtime/int)
long long, // NOLINT(runtime/int)
unsigned long long, // NOLINT(runtime/int)
int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
int64_t, uint64_t>;
TYPED_TEST_SUITE(UtilsTest, IntegerTypes);
TYPED_TEST(UtilsTest, SaturateSub) {
TypeParam min = std::numeric_limits<TypeParam>::min();
TypeParam max = std::numeric_limits<TypeParam>::max();
EXPECT_EQ(SaturateSub<TypeParam>(min, 0), min);
EXPECT_EQ(SaturateSub<TypeParam>(max, 0), max);
EXPECT_EQ(SaturateSub<TypeParam>(max, min), max);
EXPECT_EQ(SaturateSub<TypeParam>(min, max), min);
EXPECT_EQ(SaturateSub<TypeParam>(min, max / 3), min);
EXPECT_EQ(SaturateSub<TypeParam>(min + 1, 2), min);
if (std::numeric_limits<TypeParam>::is_signed) {
EXPECT_EQ(SaturateSub<TypeParam>(min, min), static_cast<TypeParam>(0));
EXPECT_EQ(SaturateSub<TypeParam>(0, min), max);
EXPECT_EQ(SaturateSub<TypeParam>(max / 3, min), max);
EXPECT_EQ(SaturateSub<TypeParam>(max / 5, min), max);
EXPECT_EQ(SaturateSub<TypeParam>(min / 3, max), min);
EXPECT_EQ(SaturateSub<TypeParam>(min / 9, max), min);
EXPECT_EQ(SaturateSub<TypeParam>(max, min / 3), max);
EXPECT_EQ(SaturateSub<TypeParam>(min, max / 3), min);
EXPECT_EQ(SaturateSub<TypeParam>(max / 3 * 2, min / 2), max);
EXPECT_EQ(SaturateSub<TypeParam>(min / 3 * 2, max / 2), min);
} else {
EXPECT_EQ(SaturateSub<TypeParam>(min, min), min);
EXPECT_EQ(SaturateSub<TypeParam>(0, min), min);
EXPECT_EQ(SaturateSub<TypeParam>(0, max), min);
EXPECT_EQ(SaturateSub<TypeParam>(max / 3, max), min);
EXPECT_EQ(SaturateSub<TypeParam>(max - 3, max), min);
}
TypeParam test_cases[] = {static_cast<TypeParam>(min / 23),
static_cast<TypeParam>(max / 3),
63,
static_cast<TypeParam>(min / 6),
static_cast<TypeParam>(max / 55),
static_cast<TypeParam>(min / 2),
static_cast<TypeParam>(max / 2),
0,
1,
2,
3,
4,
42};
TRACED_FOREACH(TypeParam, x, test_cases) {
TRACED_FOREACH(TypeParam, y, test_cases) {
if (std::numeric_limits<TypeParam>::is_signed) {
EXPECT_EQ(SaturateSub<TypeParam>(x, y), x - y);
} else {
EXPECT_EQ(SaturateSub<TypeParam>(x, y), y > x ? min : x - y);
}
}
}
}
TYPED_TEST(UtilsTest, SaturateAdd) {
TypeParam min = std::numeric_limits<TypeParam>::min();
TypeParam max = std::numeric_limits<TypeParam>::max();
EXPECT_EQ(SaturateAdd<TypeParam>(min, min), min);
EXPECT_EQ(SaturateAdd<TypeParam>(max, max), max);
EXPECT_EQ(SaturateAdd<TypeParam>(min, min / 3), min);
EXPECT_EQ(SaturateAdd<TypeParam>(max / 8 * 7, max / 3 * 2), max);
EXPECT_EQ(SaturateAdd<TypeParam>(min / 3 * 2, min / 8 * 7), min);
EXPECT_EQ(SaturateAdd<TypeParam>(max / 20 * 18, max / 25 * 18), max);
EXPECT_EQ(SaturateAdd<TypeParam>(min / 3 * 2, min / 3 * 2), min);
EXPECT_EQ(SaturateAdd<TypeParam>(max - 1, 2), max);
EXPECT_EQ(SaturateAdd<TypeParam>(max - 100, 101), max);
TypeParam test_cases[] = {static_cast<TypeParam>(min / 23),
static_cast<TypeParam>(max / 3),
63,
static_cast<TypeParam>(min / 6),
static_cast<TypeParam>(max / 55),
static_cast<TypeParam>(min / 2),
static_cast<TypeParam>(max / 2),
0,
1,
2,
3,
4,
42};
TRACED_FOREACH(TypeParam, x, test_cases) {
TRACED_FOREACH(TypeParam, y, test_cases) {
EXPECT_EQ(SaturateAdd<TypeParam>(x, y), x + y);
}
}
}
TYPED_TEST(UtilsTest, PassesFilterTest) {
EXPECT_TRUE(
PassesFilter(base::CStrVector("abcdefg"), base::CStrVector("abcdefg")));
EXPECT_TRUE(
PassesFilter(base::CStrVector("abcdefg"), base::CStrVector("abcdefg*")));
EXPECT_TRUE(
PassesFilter(base::CStrVector("abcdefg"), base::CStrVector("abc*")));
EXPECT_TRUE(PassesFilter(base::CStrVector("abcdefg"), base::CStrVector("*")));
EXPECT_TRUE(
PassesFilter(base::CStrVector("abcdefg"), base::CStrVector("-~")));
EXPECT_TRUE(
PassesFilter(base::CStrVector("abcdefg"), base::CStrVector("-abcdefgh")));
EXPECT_TRUE(PassesFilter(base::CStrVector("abdefg"), base::CStrVector("-")));
EXPECT_FALSE(
PassesFilter(base::CStrVector("abcdefg"), base::CStrVector("-abcdefg")));
EXPECT_FALSE(
PassesFilter(base::CStrVector("abcdefg"), base::CStrVector("-abcdefg*")));
EXPECT_FALSE(
PassesFilter(base::CStrVector("abcdefg"), base::CStrVector("-abc*")));
EXPECT_FALSE(
PassesFilter(base::CStrVector("abcdefg"), base::CStrVector("-*")));
EXPECT_FALSE(
PassesFilter(base::CStrVector("abcdefg"), base::CStrVector("~")));
EXPECT_FALSE(PassesFilter(base::CStrVector("abcdefg"), base::CStrVector("")));
EXPECT_FALSE(
PassesFilter(base::CStrVector("abcdefg"), base::CStrVector("abcdefgh")));
EXPECT_TRUE(PassesFilter(base::CStrVector(""), base::CStrVector("")));
EXPECT_TRUE(PassesFilter(base::CStrVector(""), base::CStrVector("*")));
EXPECT_FALSE(PassesFilter(base::CStrVector(""), base::CStrVector("-")));
EXPECT_FALSE(PassesFilter(base::CStrVector(""), base::CStrVector("-*")));
EXPECT_FALSE(PassesFilter(base::CStrVector(""), base::CStrVector("a")));
}
TEST(UtilsTest, IsInBounds) {
// for column consistency and terseness
#define INB(x, y, z) EXPECT_TRUE(base::IsInBounds<size_t>(x, y, z))
#define OOB(x, y, z) EXPECT_FALSE(base::IsInBounds<size_t>(x, y, z))
INB(0, 0, 1);
INB(0, 1, 1);
INB(1, 0, 1);
OOB(0, 2, 1);
OOB(2, 0, 1);
INB(0, 0, 2);
INB(0, 1, 2);
INB(0, 2, 2);
INB(0, 0, 2);
INB(1, 0, 2);
INB(2, 0, 2);
OOB(0, 3, 2);
OOB(3, 0, 2);
INB(0, 1, 2);
INB(1, 1, 2);
OOB(1, 2, 2);
OOB(2, 1, 2);
const size_t max = std::numeric_limits<size_t>::max();
const size_t half = max / 2;
// limit cases.
INB(0, 0, max);
INB(0, 1, max);
INB(1, 0, max);
INB(max, 0, max);
INB(0, max, max);
INB(max - 1, 0, max);
INB(0, max - 1, max);
INB(max - 1, 1, max);
INB(1, max - 1, max);
INB(half, half, max);
INB(half + 1, half, max);
INB(half, half + 1, max);
OOB(max, 0, 0);
OOB(0, max, 0);
OOB(max, 0, 1);
OOB(0, max, 1);
OOB(max, 0, 2);
OOB(0, max, 2);
OOB(max, 0, max - 1);
OOB(0, max, max - 1);
// wraparound cases.
OOB(max, 1, max);
OOB(1, max, max);
OOB(max - 1, 2, max);
OOB(2, max - 1, max);
OOB(half + 1, half + 1, max);
OOB(half + 1, half + 1, max);
#undef INB
#undef OOB
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,97 @@
// Copyright 2009 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "src/utils/version.h"
#include "src/init/v8.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace internal {
using VersionTest = ::testing::Test;
void SetVersion(int major, int minor, int build, int patch,
const char* embedder, bool candidate, const char* soname) {
Version::major_ = major;
Version::minor_ = minor;
Version::build_ = build;
Version::patch_ = patch;
Version::embedder_ = embedder;
Version::candidate_ = candidate;
Version::soname_ = soname;
}
static void CheckVersion(int major, int minor, int build, int patch,
const char* embedder, bool candidate,
const char* expected_version_string,
const char* expected_generic_soname) {
static v8::base::EmbeddedVector<char, 128> version_str;
static v8::base::EmbeddedVector<char, 128> soname_str;
// Test version without specific SONAME.
SetVersion(major, minor, build, patch, embedder, candidate, "");
Version::GetString(version_str);
CHECK_EQ(0, strcmp(expected_version_string, version_str.begin()));
Version::GetSONAME(soname_str);
CHECK_EQ(0, strcmp(expected_generic_soname, soname_str.begin()));
// Test version with specific SONAME.
const char* soname = "libv8.so.1";
SetVersion(major, minor, build, patch, embedder, candidate, soname);
Version::GetString(version_str);
CHECK_EQ(0, strcmp(expected_version_string, version_str.begin()));
Version::GetSONAME(soname_str);
CHECK_EQ(0, strcmp(soname, soname_str.begin()));
}
TEST_F(VersionTest, VersionString) {
CheckVersion(0, 0, 0, 0, "", false, "0.0.0", "libv8-0.0.0.so");
CheckVersion(0, 0, 0, 0, "", true, "0.0.0 (candidate)",
"libv8-0.0.0-candidate.so");
CheckVersion(1, 0, 0, 0, "", false, "1.0.0", "libv8-1.0.0.so");
CheckVersion(1, 0, 0, 0, "", true, "1.0.0 (candidate)",
"libv8-1.0.0-candidate.so");
CheckVersion(1, 0, 0, 1, "", false, "1.0.0.1", "libv8-1.0.0.1.so");
CheckVersion(1, 0, 0, 1, "", true, "1.0.0.1 (candidate)",
"libv8-1.0.0.1-candidate.so");
CheckVersion(2, 5, 10, 7, "", false, "2.5.10.7", "libv8-2.5.10.7.so");
CheckVersion(2, 5, 10, 7, "", true, "2.5.10.7 (candidate)",
"libv8-2.5.10.7-candidate.so");
CheckVersion(6, 0, 287, 0, "-emb.1", false, "6.0.287-emb.1",
"libv8-6.0.287-emb.1.so");
CheckVersion(6, 0, 287, 0, "-emb.1", true, "6.0.287-emb.1 (candidate)",
"libv8-6.0.287-emb.1-candidate.so");
CheckVersion(6, 0, 287, 53, "-emb.1", false, "6.0.287.53-emb.1",
"libv8-6.0.287.53-emb.1.so");
CheckVersion(6, 0, 287, 53, "-emb.1", true, "6.0.287.53-emb.1 (candidate)",
"libv8-6.0.287.53-emb.1-candidate.so");
}
} // namespace internal
} // namespace v8