v8/test/cctest/test-debug-helper.cc
Wenyu Zhao 5e0b94c4dc Allowing map word to be used for other state in GC header.
This CL adds features to pack/unpack map words.

Currently V8 cannot store extra metadata in object headers -- because V8
objects do not have a proper header, but only a map pointer at the start
of the object. To store per-object metadata like marking data, a side
table is required as the per-object metadata storage.

This CL enables V8 to use higher unused bits in a 64-bit map word as
per-object metadata storage. Map pointer stores come with an extra step
to encode the metadata into the pointer (we call it "map packing").
Map pointer loads will also remove the metadata bits as well (we call it
"map packing").

Since the map word is no longer a valid pointer after packing, we also
change the tag of the packed map word to make it looks like a Smi. This
helps various GC and barrier code to correctly skip them instead of
blindly dereferencing this invalid pointer.

A ninja flag `v8_enable_map_packing` is provided to turn this
map-packing feature on and off. It is disabled by default.

* Only works on x64 platform, with `v8_enable_pointer_compression`
  set to `false`

Bug: v8:11624
Change-Id: Ia2bdf79553945e5fc0b0874c87803d2cc733e073
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2247561
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Nico Hartmann <nicohartmann@chromium.org>
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Reviewed-by: Georg Neis <neis@chromium.org>
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#73915}
2021-04-12 17:34:13 +00:00

523 lines
23 KiB
C++

// Copyright 2018 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/api/api-inl.h"
#include "src/debug/debug.h"
#include "src/execution/frames-inl.h"
#include "src/flags/flags.h"
#include "src/heap/read-only-spaces.h"
#include "src/heap/spaces.h"
#include "test/cctest/cctest.h"
#include "tools/debug_helper/debug-helper.h"
namespace v8 {
namespace internal {
namespace {
namespace d = v8::debug_helper;
uintptr_t memory_fail_start = 0;
uintptr_t memory_fail_end = 0;
class MemoryFailureRegion {
public:
MemoryFailureRegion(uintptr_t start, uintptr_t end) {
memory_fail_start = start;
memory_fail_end = end;
}
~MemoryFailureRegion() {
memory_fail_start = 0;
memory_fail_end = 0;
}
};
// Implement the memory-reading callback. This one just fetches memory from the
// current process, but a real implementation for a debugging extension would
// fetch memory from the debuggee process or crash dump.
d::MemoryAccessResult ReadMemory(uintptr_t address, void* destination,
size_t byte_count) {
if (address >= memory_fail_start && address <= memory_fail_end) {
// Simulate failure to read debuggee memory.
return d::MemoryAccessResult::kAddressValidButInaccessible;
}
memcpy(destination, reinterpret_cast<void*>(address), byte_count);
return d::MemoryAccessResult::kOk;
}
void CheckPropBase(const d::PropertyBase& property, const char* expected_type,
const char* expected_name) {
CHECK(property.type == std::string("v8::internal::TaggedValue") ||
property.type == std::string(expected_type));
CHECK(property.decompressed_type == std::string(expected_type));
CHECK(property.name == std::string(expected_name));
}
void CheckProp(const d::ObjectProperty& property, const char* expected_type,
const char* expected_name,
d::PropertyKind expected_kind = d::PropertyKind::kSingle,
size_t expected_num_values = 1) {
CheckPropBase(property, expected_type, expected_name);
CHECK_EQ(property.num_values, expected_num_values);
CHECK(property.kind == expected_kind);
}
template <typename TValue>
void CheckProp(const d::ObjectProperty& property, const char* expected_type,
const char* expected_name, TValue expected_value) {
CheckProp(property, expected_type, expected_name);
CHECK(*reinterpret_cast<TValue*>(property.address) == expected_value);
}
bool StartsWith(const std::string& full_string, const std::string& prefix) {
return full_string.substr(0, prefix.size()) == prefix;
}
bool Contains(const std::string& full_string, const std::string& substr) {
return full_string.find(substr) != std::string::npos;
}
void CheckStructProp(const d::StructProperty& property,
const char* expected_type, const char* expected_name,
size_t expected_offset, uint8_t expected_num_bits = 0,
uint8_t expected_shift_bits = 0) {
CheckPropBase(property, expected_type, expected_name);
CHECK_EQ(property.offset, expected_offset);
CHECK_EQ(property.num_bits, expected_num_bits);
CHECK_EQ(property.shift_bits, expected_shift_bits);
}
const d::ObjectProperty& FindProp(const d::ObjectPropertiesResult& props,
std::string name) {
for (size_t i = 0; i < props.num_properties; ++i) {
if (name == props.properties[i]->name) {
return *props.properties[i];
}
}
CHECK_WITH_MSG(false, ("property '" + name + "' not found").c_str());
UNREACHABLE();
}
template <typename TValue>
TValue ReadProp(const d::ObjectPropertiesResult& props, std::string name) {
const d::ObjectProperty& prop = FindProp(props, name);
return *reinterpret_cast<TValue*>(prop.address);
}
// A simple implementation of ExternalStringResource that lets us control the
// result of IsCacheable().
class StringResource : public v8::String::ExternalStringResource {
public:
explicit StringResource(bool cacheable) : cacheable_(cacheable) {}
const uint16_t* data() const override {
return reinterpret_cast<const uint16_t*>(u"abcde");
}
size_t length() const override { return 5; }
bool IsCacheable() const override { return cacheable_; }
private:
bool cacheable_;
};
} // namespace
TEST(GetObjectProperties) {
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
v8::HandleScope scope(isolate);
LocalContext context;
// Claim we don't know anything about the heap layout.
d::HeapAddresses heap_addresses{0, 0, 0, 0};
v8::Local<v8::Value> v = CompileRun("42");
Handle<Object> o = v8::Utils::OpenHandle(*v);
d::ObjectPropertiesResultPtr props =
d::GetObjectProperties(o->ptr(), &ReadMemory, heap_addresses);
CHECK(props->type_check_result == d::TypeCheckResult::kSmi);
CHECK(props->brief == std::string("42 (0x2a)"));
CHECK(props->type == std::string("v8::internal::Smi"));
CHECK_EQ(props->num_properties, 0);
v = CompileRun("[\"a\", \"bc\"]");
o = v8::Utils::OpenHandle(*v);
props = d::GetObjectProperties(o->ptr(), &ReadMemory, heap_addresses);
CHECK(props->type_check_result == d::TypeCheckResult::kUsedMap);
CHECK(props->type == std::string("v8::internal::JSArray"));
CHECK_EQ(props->num_properties, 4);
CheckProp(*props->properties[0], "v8::internal::Map", "map");
CheckProp(*props->properties[1], "v8::internal::Object",
"properties_or_hash");
CheckProp(*props->properties[2], "v8::internal::FixedArrayBase", "elements");
CheckProp(*props->properties[3], "v8::internal::Object", "length",
static_cast<i::Tagged_t>(IntToSmi(2)));
// We need to supply some valid address for decompression before reading the
// elements from the JSArray.
heap_addresses.any_heap_pointer = o->ptr();
i::Tagged_t properties_or_hash =
*reinterpret_cast<i::Tagged_t*>(props->properties[1]->address);
i::Tagged_t elements =
*reinterpret_cast<i::Tagged_t*>(props->properties[2]->address);
// The properties_or_hash_code field should be an empty fixed array. Since
// that is at a known offset, we should be able to detect it even without
// any ability to read memory.
{
MemoryFailureRegion failure(0, UINTPTR_MAX);
props =
d::GetObjectProperties(properties_or_hash, &ReadMemory, heap_addresses);
CHECK(props->type_check_result ==
d::TypeCheckResult::kObjectPointerValidButInaccessible);
CHECK(props->type == std::string("v8::internal::HeapObject"));
CHECK_EQ(props->num_properties, 1);
CheckProp(*props->properties[0], "v8::internal::Map", "map");
// "maybe" prefix indicates that GetObjectProperties recognized the offset
// within the page as matching a known object, but didn't know whether the
// object is on the right page. This response can only happen in builds
// without pointer compression, because otherwise heap addresses would be at
// deterministic locations within the heap reservation.
CHECK(COMPRESS_POINTERS_BOOL
? StartsWith(props->brief, "EmptyFixedArray")
: Contains(props->brief, "maybe EmptyFixedArray"));
// Provide a heap first page so the API can be more sure.
heap_addresses.read_only_space_first_page =
i_isolate->heap()->read_only_space()->FirstPageAddress();
props =
d::GetObjectProperties(properties_or_hash, &ReadMemory, heap_addresses);
CHECK(props->type_check_result ==
d::TypeCheckResult::kObjectPointerValidButInaccessible);
CHECK(props->type == std::string("v8::internal::HeapObject"));
CHECK_EQ(props->num_properties, 1);
CheckProp(*props->properties[0], "v8::internal::Map", "map");
CHECK(StartsWith(props->brief, "EmptyFixedArray"));
}
props = d::GetObjectProperties(elements, &ReadMemory, heap_addresses);
CHECK(props->type_check_result == d::TypeCheckResult::kUsedMap);
CHECK(props->type == std::string("v8::internal::FixedArray"));
CHECK_EQ(props->num_properties, 3);
CheckProp(*props->properties[0], "v8::internal::Map", "map");
CheckProp(*props->properties[1], "v8::internal::Object", "length",
static_cast<i::Tagged_t>(IntToSmi(2)));
CheckProp(*props->properties[2], "v8::internal::Object", "objects",
d::PropertyKind::kArrayOfKnownSize, 2);
// Get the second string value from the FixedArray.
i::Tagged_t second_string_address =
reinterpret_cast<i::Tagged_t*>(props->properties[2]->address)[1];
props = d::GetObjectProperties(second_string_address, &ReadMemory,
heap_addresses);
CHECK(props->type_check_result == d::TypeCheckResult::kUsedMap);
CHECK(props->type == std::string("v8::internal::SeqOneByteString"));
CHECK_EQ(props->num_properties, 4);
CheckProp(*props->properties[0], "v8::internal::Map", "map");
CheckProp(*props->properties[1], "uint32_t", "raw_hash_field");
CheckProp(*props->properties[2], "int32_t", "length", 2);
CheckProp(*props->properties[3], "char", "chars",
d::PropertyKind::kArrayOfKnownSize, 2);
CHECK_EQ(
strncmp("bc",
reinterpret_cast<const char*>(props->properties[3]->address), 2),
0);
// Read the second string again, using a type hint instead of the map. All of
// its properties should match what we read last time.
d::ObjectPropertiesResultPtr props2;
{
heap_addresses.read_only_space_first_page = 0;
uintptr_t map_ptr = props->properties[0]->address;
uintptr_t map_map_ptr = *reinterpret_cast<i::Tagged_t*>(map_ptr);
#if V8_MAP_PACKING
map_map_ptr = reinterpret_cast<i::MapWord*>(&map_map_ptr)->ToMap().ptr();
#endif
uintptr_t map_address =
d::GetObjectProperties(map_map_ptr, &ReadMemory, heap_addresses)
->properties[0]
->address;
MemoryFailureRegion failure(map_address, map_address + i::Map::kSize);
props2 = d::GetObjectProperties(second_string_address, &ReadMemory,
heap_addresses, "v8::internal::String");
if (COMPRESS_POINTERS_BOOL) {
// The first page of each heap space can be automatically detected when
// pointer compression is active, so we expect to use known maps instead
// of the type hint.
CHECK_EQ(props2->type_check_result, d::TypeCheckResult::kKnownMapPointer);
CHECK(props2->type == std::string("v8::internal::SeqOneByteString"));
CHECK_EQ(props2->num_properties, 4);
CheckProp(*props2->properties[3], "char", "chars",
d::PropertyKind::kArrayOfKnownSize, 2);
CHECK_EQ(props2->num_guessed_types, 0);
} else {
CHECK_EQ(props2->type_check_result, d::TypeCheckResult::kUsedTypeHint);
CHECK(props2->type == std::string("v8::internal::String"));
CHECK_EQ(props2->num_properties, 3);
// The type hint we provided was the abstract class String, but
// GetObjectProperties should have recognized that the Map pointer looked
// like the right value for a SeqOneByteString.
CHECK_EQ(props2->num_guessed_types, 1);
CHECK(std::string(props2->guessed_types[0]) ==
std::string("v8::internal::SeqOneByteString"));
}
CheckProp(*props2->properties[0], "v8::internal::Map", "map",
*reinterpret_cast<i::Tagged_t*>(props->properties[0]->address));
CheckProp(*props2->properties[1], "uint32_t", "raw_hash_field",
*reinterpret_cast<int32_t*>(props->properties[1]->address));
CheckProp(*props2->properties[2], "int32_t", "length", 2);
}
// Try a weak reference.
props2 = d::GetObjectProperties(second_string_address | kWeakHeapObjectMask,
&ReadMemory, heap_addresses);
std::string weak_ref_prefix = "weak ref to ";
CHECK(weak_ref_prefix + props->brief == props2->brief);
CHECK(props2->type_check_result == d::TypeCheckResult::kUsedMap);
CHECK(props2->type == std::string("v8::internal::SeqOneByteString"));
CHECK_EQ(props2->num_properties, 4);
CheckProp(*props2->properties[0], "v8::internal::Map", "map",
*reinterpret_cast<i::Tagged_t*>(props->properties[0]->address));
CheckProp(*props2->properties[1], "uint32_t", "raw_hash_field",
*reinterpret_cast<i::Tagged_t*>(props->properties[1]->address));
CheckProp(*props2->properties[2], "int32_t", "length", 2);
// Build a complicated string (multi-level cons with slices inside) to test
// string printing.
v = CompileRun(R"(
const alphabet = "abcdefghijklmnopqrstuvwxyz";
alphabet.substr(3,20) + alphabet.toUpperCase().substr(5,15) + "7")");
o = v8::Utils::OpenHandle(*v);
props = d::GetObjectProperties(o->ptr(), &ReadMemory, heap_addresses);
CHECK(Contains(props->brief, "\"defghijklmnopqrstuvwFGHIJKLMNOPQRST7\""));
// Cause a failure when reading the "second" pointer within the top-level
// ConsString.
{
CheckProp(*props->properties[4], "v8::internal::String", "second");
uintptr_t second_address = props->properties[4]->address;
MemoryFailureRegion failure(second_address, second_address + 4);
props = d::GetObjectProperties(o->ptr(), &ReadMemory, heap_addresses);
CHECK(Contains(props->brief, "\"defghijklmnopqrstuvwFGHIJKLMNOPQRST...\""));
}
// Build a very long string.
v = CompileRun("'a'.repeat(1000)");
o = v8::Utils::OpenHandle(*v);
props = d::GetObjectProperties(o->ptr(), &ReadMemory, heap_addresses);
CHECK(Contains(props->brief, "\"" + std::string(80, 'a') + "...\""));
// GetObjectProperties can read cacheable external strings.
auto external_string =
v8::String::NewExternalTwoByte(isolate, new StringResource(true));
o = v8::Utils::OpenHandle(*external_string.ToLocalChecked());
props = d::GetObjectProperties(o->ptr(), &ReadMemory, heap_addresses);
CHECK(Contains(props->brief, "\"abcde\""));
// GetObjectProperties cannot read uncacheable external strings.
external_string =
v8::String::NewExternalTwoByte(isolate, new StringResource(false));
o = v8::Utils::OpenHandle(*external_string.ToLocalChecked());
props = d::GetObjectProperties(o->ptr(), &ReadMemory, heap_addresses);
CHECK_EQ(std::string(props->brief).find("\""), std::string::npos);
// Build a basic JS object and get its properties.
v = CompileRun("({a: 1, b: 2})");
o = v8::Utils::OpenHandle(*v);
props = d::GetObjectProperties(o->ptr(), &ReadMemory, heap_addresses);
// Objects constructed from literals get their properties placed inline, so
// the GetObjectProperties response should include an array.
const d::ObjectProperty& prop = FindProp(*props, "in-object properties");
CheckProp(prop, "v8::internal::Object", "in-object properties",
d::PropertyKind::kArrayOfKnownSize, 2);
// The second item in that array is the SMI value 2 from the object literal.
props2 =
d::GetObjectProperties(reinterpret_cast<i::Tagged_t*>(prop.address)[1],
&ReadMemory, heap_addresses);
CHECK(props2->brief == std::string("2 (0x2)"));
// Verify the result for a heap object field which is itself a struct: the
// "descriptors" field on a DescriptorArray.
// Start by getting the object's map and the map's descriptor array.
uintptr_t map_ptr = ReadProp<i::Tagged_t>(*props, "map");
#if V8_MAP_PACKING
map_ptr = reinterpret_cast<i::MapWord*>(&map_ptr)->ToMap().ptr();
#endif
props = d::GetObjectProperties(map_ptr, &ReadMemory, heap_addresses);
props = d::GetObjectProperties(
ReadProp<i::Tagged_t>(*props, "instance_descriptors"), &ReadMemory,
heap_addresses);
// It should have at least two descriptors (possibly plus slack).
CheckProp(*props->properties[1], "uint16_t", "number_of_all_descriptors");
uint16_t number_of_all_descriptors =
*reinterpret_cast<uint16_t*>(props->properties[1]->address);
CHECK_GE(number_of_all_descriptors, 2);
// The "descriptors" property should describe the struct layout for each
// element in the array.
const d::ObjectProperty& descriptors = *props->properties[6];
// No C++ type is reported directly because there may not be an actual C++
// struct with this layout, hence the empty string in this check.
CheckProp(descriptors, /*type=*/"", "descriptors",
d::PropertyKind::kArrayOfKnownSize, number_of_all_descriptors);
CHECK_EQ(descriptors.size, 3 * i::kTaggedSize);
CHECK_EQ(descriptors.num_struct_fields, 3);
CheckStructProp(*descriptors.struct_fields[0],
"v8::internal::PrimitiveHeapObject", "key",
0 * i::kTaggedSize);
CheckStructProp(*descriptors.struct_fields[1], "v8::internal::Object",
"details", 1 * i::kTaggedSize);
CheckStructProp(*descriptors.struct_fields[2], "v8::internal::Object",
"value", 2 * i::kTaggedSize);
// Build a basic JS function and get its properties. This will allow us to
// exercise bitfield functionality.
v = CompileRun("(function () {})");
o = v8::Utils::OpenHandle(*v);
props = d::GetObjectProperties(o->ptr(), &ReadMemory, heap_addresses);
props = d::GetObjectProperties(
ReadProp<i::Tagged_t>(*props, "shared_function_info"), &ReadMemory,
heap_addresses);
const d::ObjectProperty& flags = FindProp(*props, "flags");
CHECK_GE(flags.num_struct_fields, 3);
CheckStructProp(*flags.struct_fields[0], "FunctionKind", "function_kind", 0,
5, 0);
CheckStructProp(*flags.struct_fields[1], "bool", "is_native", 0, 1, 5);
CheckStructProp(*flags.struct_fields[2], "bool", "is_strict", 0, 1, 6);
// Get data about a different bitfield struct which is contained within a smi.
Handle<i::JSFunction> function = Handle<i::JSFunction>::cast(o);
Handle<i::SharedFunctionInfo> shared(function->shared(), i_isolate);
Handle<i::DebugInfo> debug_info =
i_isolate->debug()->GetOrCreateDebugInfo(shared);
props =
d::GetObjectProperties(debug_info->ptr(), &ReadMemory, heap_addresses);
const d::ObjectProperty& debug_flags = FindProp(*props, "flags");
CHECK_GE(debug_flags.num_struct_fields, 5);
CheckStructProp(*debug_flags.struct_fields[0], "bool", "has_break_info", 0, 1,
i::kSmiTagSize + i::kSmiShiftSize);
CheckStructProp(*debug_flags.struct_fields[4], "bool", "can_break_at_entry",
0, 1, i::kSmiTagSize + i::kSmiShiftSize + 4);
}
TEST(ListObjectClasses) {
CcTest::InitializeVM();
// The ListObjectClasses result will change as classes are added, removed, or
// renamed. Just check that a few expected classes are included in the list,
// and that there are no duplicates.
const d::ClassList* class_list = d::ListObjectClasses();
std::unordered_set<std::string> class_set;
for (size_t i = 0; i < class_list->num_class_names; ++i) {
CHECK_WITH_MSG(class_set.insert(class_list->class_names[i]).second,
"there should be no duplicate entries");
}
CHECK_NE(class_set.find("v8::internal::HeapObject"), class_set.end());
CHECK_NE(class_set.find("v8::internal::String"), class_set.end());
CHECK_NE(class_set.find("v8::internal::JSRegExp"), class_set.end());
}
static void FrameIterationCheck(
v8::Local<v8::String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
i::StackFrameIterator iter(reinterpret_cast<i::Isolate*>(info.GetIsolate()));
for (int i = 0; !iter.done(); i++) {
i::StackFrame* frame = iter.frame();
CHECK(i != 0 || (frame->type() == i::StackFrame::EXIT));
d::StackFrameResultPtr props = d::GetStackFrame(frame->fp(), &ReadMemory);
if (frame->is_java_script()) {
JavaScriptFrame* js_frame = JavaScriptFrame::cast(frame);
CHECK_EQ(props->num_properties, 5);
auto js_function = js_frame->function();
CheckProp(*props->properties[0], "v8::internal::JSFunction",
"currently_executing_jsfunction", js_function.ptr());
auto shared_function_info = js_function.shared();
auto script = i::Script::cast(shared_function_info.script());
CheckProp(*props->properties[1], "v8::internal::Object", "script_name",
static_cast<i::Tagged_t>(script.name().ptr()));
CheckProp(*props->properties[2], "v8::internal::Object", "script_source",
static_cast<i::Tagged_t>(script.source().ptr()));
auto scope_info = shared_function_info.scope_info();
CheckProp(*props->properties[3], "v8::internal::Object", "function_name",
static_cast<i::Tagged_t>(scope_info.FunctionName().ptr()));
CheckProp(*props->properties[4], "", "function_character_offset");
const d::ObjectProperty& function_character_offset =
*props->properties[4];
CHECK_EQ(function_character_offset.num_struct_fields, 2);
CheckStructProp(*function_character_offset.struct_fields[0],
"v8::internal::Object", "start", 0);
CheckStructProp(*function_character_offset.struct_fields[1],
"v8::internal::Object", "end", 4);
} else {
CHECK_EQ(props->num_properties, 0);
}
iter.Advance();
}
}
THREADED_TEST(GetFrameStack) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(isolate);
obj->SetAccessor(v8_str("xxx"), FrameIterationCheck);
CHECK(env->Global()
->Set(env.local(), v8_str("obj"),
obj->NewInstance(env.local()).ToLocalChecked())
.FromJust());
v8::Script::Compile(env.local(), v8_str("function foo() {"
" return obj.xxx;"
"}"
"foo();"))
.ToLocalChecked()
->Run(env.local())
.ToLocalChecked();
}
TEST(SmallOrderedHashSetGetObjectProperties) {
LocalContext context;
Isolate* isolate = reinterpret_cast<Isolate*>((*context)->GetIsolate());
Factory* factory = isolate->factory();
HandleScope scope(isolate);
Handle<SmallOrderedHashSet> set = factory->NewSmallOrderedHashSet();
const size_t number_of_buckets = 2;
CHECK_EQ(number_of_buckets, set->NumberOfBuckets());
CHECK_EQ(0, set->NumberOfElements());
// Verify with the definition of SmallOrderedHashSet in
// src\objects\ordered-hash-table.tq.
d::HeapAddresses heap_addresses{0, 0, 0, 0};
d::ObjectPropertiesResultPtr props =
d::GetObjectProperties(set->ptr(), &ReadMemory, heap_addresses);
CHECK_EQ(props->type_check_result, d::TypeCheckResult::kUsedMap);
CHECK_EQ(props->type, std::string("v8::internal::SmallOrderedHashSet"));
CHECK_EQ(props->num_properties, 8);
CheckProp(*props->properties[0], "v8::internal::Map", "map");
CheckProp(*props->properties[1], "uint8_t", "number_of_elements");
CheckProp(*props->properties[2], "uint8_t", "number_of_deleted_elements");
CheckProp(*props->properties[3], "uint8_t", "number_of_buckets");
#if TAGGED_SIZE_8_BYTES
CheckProp(*props->properties[4], "uint8_t", "padding",
d::PropertyKind::kArrayOfKnownSize, 5);
#else
CheckProp(*props->properties[4], "uint8_t", "padding",
d::PropertyKind::kArrayOfKnownSize, 1);
#endif
CheckProp(*props->properties[5], "v8::internal::Object", "data_table",
d::PropertyKind::kArrayOfKnownSize,
number_of_buckets * OrderedHashMap::kLoadFactor);
CheckProp(*props->properties[6], "uint8_t", "hash_table",
d::PropertyKind::kArrayOfKnownSize, number_of_buckets);
CheckProp(*props->properties[7], "uint8_t", "chain_table",
d::PropertyKind::kArrayOfKnownSize,
number_of_buckets * OrderedHashMap::kLoadFactor);
}
} // namespace internal
} // namespace v8