Commit 3d2501b2 authored by Santiago Aboy Solanes's avatar Santiago Aboy Solanes Committed by Commit Bot

[compiler] Add cctest for prototype chain walk

Bug: v8:7790
Change-Id: I1b9116529575f56c890f93488a0ffdebfdfe5763
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2260873
Commit-Queue: Santiago Aboy Solanes <solanes@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68649}
parent 4d655870
......@@ -238,7 +238,7 @@ class JSReceiver : public HeapObject {
LookupIterator* it);
// Set the object's prototype (only JSReceiver and null are allowed values).
V8_WARN_UNUSED_RESULT static Maybe<bool> SetPrototype(
V8_EXPORT_PRIVATE V8_WARN_UNUSED_RESULT static Maybe<bool> SetPrototype(
Handle<JSReceiver> object, Handle<Object> value, bool from_javascript,
ShouldThrow should_throw);
......
......@@ -197,6 +197,7 @@ v8_source_set("cctest_sources") {
"test-code-stub-assembler.cc",
"test-compiler.cc",
"test-concurrent-descriptor-array.cc",
"test-concurrent-prototype.cc",
"test-constantpool.cc",
"test-conversions.cc",
"test-cpu-profiler.cc",
......
// Copyright 2020 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.h"
#include "src/base/platform/semaphore.h"
#include "src/handles/handles-inl.h"
#include "src/handles/local-handles-inl.h"
#include "src/handles/persistent-handles.h"
#include "src/heap/heap.h"
#include "src/heap/local-heap.h"
#include "test/cctest/cctest.h"
#include "test/cctest/heap/heap-utils.h"
namespace v8 {
namespace internal {
static constexpr int kNumHandles = kHandleBlockSize * 2 + kHandleBlockSize / 2;
namespace {
class ConcurrentSearchThread final : public v8::base::Thread {
public:
ConcurrentSearchThread(Heap* heap, std::vector<Handle<JSObject>> handles,
std::unique_ptr<PersistentHandles> ph,
base::Semaphore* sema_started)
: v8::base::Thread(base::Thread::Options("ThreadWithLocalHeap")),
heap_(heap),
handles_(std::move(handles)),
ph_(std::move(ph)),
sema_started_(sema_started) {}
void Run() override {
LocalHeap local_heap(heap_, std::move(ph_));
LocalHandleScope scope(&local_heap);
Address object = handles_[0]->ptr();
for (int i = 0; i < kNumHandles; i++) {
handles_.push_back(
Handle<JSObject>::cast(local_heap.NewPersistentHandle(object)));
}
sema_started_->Signal();
for (Handle<JSObject> js_obj : handles_) {
// Walk up the prototype chain all the way to the top.
Handle<Map> map(js_obj->synchronized_map(), &local_heap);
while (!map->prototype().IsNull()) {
Handle<Map> map_prototype_map(map->prototype().synchronized_map(),
&local_heap);
if (!map_prototype_map->IsJSObjectMap()) {
break;
}
map = map_prototype_map;
}
}
CHECK_EQ(handles_.size(), kNumHandles * 2);
CHECK(!ph_);
ph_ = local_heap.DetachPersistentHandles();
}
Heap* heap_;
std::vector<Handle<JSObject>> handles_;
std::unique_ptr<PersistentHandles> ph_;
base::Semaphore* sema_started_;
};
// Test to search on a background thread, while the main thread is idle.
TEST(ProtoWalkBackground) {
CcTest::InitializeVM();
FLAG_local_heaps = true;
Isolate* isolate = CcTest::i_isolate();
std::unique_ptr<PersistentHandles> ph = isolate->NewPersistentHandles();
std::vector<Handle<JSObject>> handles;
auto factory = isolate->factory();
HandleScope handle_scope(isolate);
Handle<JSFunction> function =
factory->NewFunctionForTest(factory->empty_string());
Handle<JSObject> js_object = factory->NewJSObject(function);
Handle<String> name = CcTest::MakeString("property");
Handle<Object> value = CcTest::MakeString("dummy_value");
// For the default constructor function no in-object properties are reserved
// hence adding a single property will initialize the property-array.
JSObject::DefinePropertyOrElementIgnoreAttributes(js_object, name, value,
NONE)
.Check();
Address object = js_object->ptr();
for (int i = 0; i < kNumHandles; i++) {
handles.push_back(Handle<JSObject>::cast(ph->NewHandle(object)));
}
base::Semaphore sema_started(0);
// Pass persistent handles to background thread.
std::unique_ptr<ConcurrentSearchThread> thread(new ConcurrentSearchThread(
isolate->heap(), std::move(handles), std::move(ph), &sema_started));
CHECK(thread->Start());
sema_started.Wait();
thread->Join();
}
// Test to search on a background thread, while the main thread modifies the
// descriptor array.
TEST(ProtoWalkBackground_DescriptorArrayWrite) {
CcTest::InitializeVM();
FLAG_local_heaps = true;
Isolate* isolate = CcTest::i_isolate();
std::unique_ptr<PersistentHandles> ph = isolate->NewPersistentHandles();
std::vector<Handle<JSObject>> handles;
auto factory = isolate->factory();
HandleScope handle_scope(isolate);
Handle<JSFunction> function =
factory->NewFunctionForTest(factory->empty_string());
Handle<JSObject> js_object = factory->NewJSObject(function);
Handle<String> name = CcTest::MakeString("property");
Handle<Object> value = CcTest::MakeString("dummy_value");
// For the default constructor function no in-object properties are reserved
// hence adding a single property will initialize the property-array.
JSObject::DefinePropertyOrElementIgnoreAttributes(js_object, name, value,
NONE)
.Check();
Address object = js_object->ptr();
for (int i = 0; i < kNumHandles; i++) {
handles.push_back(Handle<JSObject>::cast(ph->NewHandle(object)));
}
base::Semaphore sema_started(0);
// Pass persistent handles to background thread.
std::unique_ptr<ConcurrentSearchThread> thread(new ConcurrentSearchThread(
isolate->heap(), std::move(handles), std::move(ph), &sema_started));
CHECK(thread->Start());
sema_started.Wait();
// Exercise descriptor array.
for (int i = 0; i < 20; ++i) {
Handle<String> filler_name = CcTest::MakeName("filler_property_", i);
Handle<Object> filler_value = CcTest::MakeString("dummy_value");
JSObject::DefinePropertyOrElementIgnoreAttributes(js_object, filler_name,
filler_value, NONE)
.Check();
}
thread->Join();
}
TEST(ProtoWalkBackground_PrototypeChainWrite) {
CcTest::InitializeVM();
FLAG_local_heaps = true;
Isolate* isolate = CcTest::i_isolate();
std::unique_ptr<PersistentHandles> ph = isolate->NewPersistentHandles();
std::vector<Handle<JSObject>> handles;
auto factory = isolate->factory();
HandleScope handle_scope(isolate);
Handle<JSFunction> function =
factory->NewFunctionForTest(factory->empty_string());
Handle<JSObject> js_object = factory->NewJSObject(function);
Address object = js_object->ptr();
for (int i = 0; i < kNumHandles; i++) {
handles.push_back(Handle<JSObject>::cast(ph->NewHandle(object)));
}
base::Semaphore sema_started(0);
// Pass persistent handles to background thread.
std::unique_ptr<ConcurrentSearchThread> thread(new ConcurrentSearchThread(
isolate->heap(), std::move(handles), std::move(ph), &sema_started));
CHECK(thread->Start());
// The prototype chain looks like this JSObject -> Object -> null. Change the
// prototype of the js_object to be JSObject -> null, and then back a bunch of
// times.
Handle<Map> map(js_object->map(), isolate);
Handle<HeapObject> old_proto(map->prototype(), isolate);
DCHECK(!old_proto->IsNull());
Handle<HeapObject> new_proto(old_proto->map().prototype(), isolate);
sema_started.Wait();
for (int i = 0; i < 20; ++i) {
CHECK(JSReceiver::SetPrototype(
js_object, i % 2 == 0 ? new_proto : old_proto, false, kDontThrow)
.FromJust());
}
thread->Join();
}
} // anonymous namespace
} // namespace internal
} // namespace v8
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment