32b911f96a
This reverts commitf78d69fa5d
. With https://chromium-review.googlesource.com/c/v8/v8/+/2243216, incorrect MemoryChunk::FromHeapObject uses are now fixed. Original change's description: > Revert "[heap] Make ReadOnlySpace use bump pointer allocation" > > This reverts commit81c34968a7
and also >490f3580a3
which depends on the former. > > Reason for revert: Break CFI tests in chromium https://ci.chromium.org/p/chromium/builders/ci/Linux%20CFI/17438 > Original change's description: > > [heap] Make ReadOnlySpace use bump pointer allocation > > > > This changes ReadOnlySpace to no longer be a PagedSpace but instead it > > is now a BaseSpace. BasicSpace is a new base class that Space inherits > > from and which has no allocation methods and does not dictate how the > > pages should be held. > > > > ReadOnlySpace unlike Space holds its pages as a > > std::vector<ReadOnlyPage>, where ReadOnlyPage directly subclasses > > BasicMemoryChunk, meaning they do not have prev_ and next_ pointers and > > cannot be held in a heap::List. This is desirable since with pointer > > compression we would like to remap these pages to different memory > > addresses which would be impossible with a heap::List. > > > > Since ReadOnlySpace no longer uses most of the code from the other > > Spaces it makes sense to simplify its memory allocation to use a simple > > bump pointer and always allocate a new page whenever an allocation > > exceeds the remaining space on the final page. > > > > Change-Id: Iee6d9f96cfb174b4026ee671ee4f897909b38418 > > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2209060 > > Commit-Queue: Dan Elphick <delphick@chromium.org> > > Reviewed-by: Ulan Degenbaev <ulan@chromium.org> > > Cr-Commit-Position: refs/heads/master@{#68137} > > TBR=ulan@chromium.org,delphick@chromium.org > > # Not skipping CQ checks because original CL landed > 1 day ago. > > Change-Id: I68c9834872e55eb833be081f8ff99b786bfa9894 > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2232552 > Commit-Queue: Dan Elphick <delphick@chromium.org> > Reviewed-by: Dan Elphick <delphick@chromium.org> > Reviewed-by: Ulan Degenbaev <ulan@chromium.org> > Cr-Commit-Position: refs/heads/master@{#68211} TBR=ulan@chromium.org,delphick@chromium.org # Not skipping CQ checks because original CL landed > 1 day ago. Change-Id: Id5b3cce41b5dec1dca816c05848d183790b1cc05 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2250254 Reviewed-by: Dan Elphick <delphick@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Commit-Queue: Dan Elphick <delphick@chromium.org> Cr-Commit-Position: refs/heads/master@{#68407}
415 lines
18 KiB
C++
415 lines
18 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/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, uint8_t* 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", "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_address =
|
|
d::GetObjectProperties(
|
|
*reinterpret_cast<i::Tagged_t*>(props->properties[0]->address),
|
|
&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", "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", "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.
|
|
props = d::GetObjectProperties(ReadProp<i::Tagged_t>(*props, "map"),
|
|
&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());
|
|
}
|
|
|
|
} // namespace internal
|
|
} // namespace v8
|