Commit a3977a91 authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[cctest] Add simple test for EnumCache

This test documents the basic behavior of the EnumCache which is shared
on the DescriptorArray.

Change-Id: Idd40670d99d81bb5e4b6161ffc47f2898ca9d2a9
Reviewed-on: https://chromium-review.googlesource.com/643297
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47735}
parent 5dfacfed
......@@ -73,5 +73,179 @@ TEST(NoSideEffectsToString) {
"#<Object>");
}
TEST(EnumCache) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
i::Factory* factory = CcTest::i_isolate()->factory();
v8::HandleScope scope(isolate);
// Create a nice transition tree:
// (a) --> (b) --> (c) shared DescriptorArray 1
// |
// +---> (cc) shared DescriptorArray 2
CompileRun(
"function O(a) { this.a = 1 };"
"a = new O();"
"b = new O();"
"b.b = 2;"
"c = new O();"
"c.b = 2;"
"c.c = 3;"
"cc = new O();"
"cc.b = 2;"
"cc.cc = 4;");
Handle<JSObject> a = Handle<JSObject>::cast(v8::Utils::OpenHandle(
*env->Global()->Get(env.local(), v8_str("a")).ToLocalChecked()));
Handle<JSObject> b = Handle<JSObject>::cast(v8::Utils::OpenHandle(
*env->Global()->Get(env.local(), v8_str("b")).ToLocalChecked()));
Handle<JSObject> c = Handle<JSObject>::cast(v8::Utils::OpenHandle(
*env->Global()->Get(env.local(), v8_str("c")).ToLocalChecked()));
Handle<JSObject> cc = Handle<JSObject>::cast(v8::Utils::OpenHandle(
*env->Global()->Get(env.local(), v8_str("cc")).ToLocalChecked()));
// Check the transition tree.
CHECK_EQ(a->map()->instance_descriptors(), b->map()->instance_descriptors());
CHECK_EQ(b->map()->instance_descriptors(), c->map()->instance_descriptors());
CHECK_NE(c->map()->instance_descriptors(), cc->map()->instance_descriptors());
CHECK_NE(b->map()->instance_descriptors(), cc->map()->instance_descriptors());
// Check that the EnumLength is unset.
CHECK_EQ(a->map()->EnumLength(), kInvalidEnumCacheSentinel);
CHECK_EQ(b->map()->EnumLength(), kInvalidEnumCacheSentinel);
CHECK_EQ(c->map()->EnumLength(), kInvalidEnumCacheSentinel);
CHECK_EQ(cc->map()->EnumLength(), kInvalidEnumCacheSentinel);
// Check that the EnumCache is empty.
CHECK_EQ(a->map()->instance_descriptors()->GetEnumCache(),
*factory->empty_enum_cache());
CHECK_EQ(b->map()->instance_descriptors()->GetEnumCache(),
*factory->empty_enum_cache());
CHECK_EQ(c->map()->instance_descriptors()->GetEnumCache(),
*factory->empty_enum_cache());
CHECK_EQ(cc->map()->instance_descriptors()->GetEnumCache(),
*factory->empty_enum_cache());
// The EnumCache is shared on the DescriptorArray, creating it on {cc} has no
// effect on the other maps.
CompileRun("var s = 0; for (let key in cc) { s += cc[key] };");
{
CHECK_EQ(a->map()->EnumLength(), kInvalidEnumCacheSentinel);
CHECK_EQ(b->map()->EnumLength(), kInvalidEnumCacheSentinel);
CHECK_EQ(c->map()->EnumLength(), kInvalidEnumCacheSentinel);
CHECK_EQ(cc->map()->EnumLength(), 3);
CHECK_EQ(a->map()->instance_descriptors()->GetEnumCache(),
*factory->empty_enum_cache());
CHECK_EQ(b->map()->instance_descriptors()->GetEnumCache(),
*factory->empty_enum_cache());
CHECK_EQ(c->map()->instance_descriptors()->GetEnumCache(),
*factory->empty_enum_cache());
EnumCache* enum_cache = cc->map()->instance_descriptors()->GetEnumCache();
CHECK_NE(enum_cache, *factory->empty_enum_cache());
CHECK_EQ(enum_cache->keys()->length(), 3);
CHECK_EQ(enum_cache->indices()->length(), 3);
}
// Initializing the EnumCache for the the topmost map {a} will not create the
// cache for the other maps.
CompileRun("var s = 0; for (let key in a) { s += a[key] };");
{
CHECK_EQ(a->map()->EnumLength(), 1);
CHECK_EQ(b->map()->EnumLength(), kInvalidEnumCacheSentinel);
CHECK_EQ(c->map()->EnumLength(), kInvalidEnumCacheSentinel);
CHECK_EQ(cc->map()->EnumLength(), 3);
// The enum cache is shared on the descriptor array of maps {a}, {b} and
// {c} only.
EnumCache* enum_cache = a->map()->instance_descriptors()->GetEnumCache();
CHECK_NE(enum_cache, *factory->empty_enum_cache());
CHECK_NE(cc->map()->instance_descriptors()->GetEnumCache(),
*factory->empty_enum_cache());
CHECK_NE(cc->map()->instance_descriptors()->GetEnumCache(), enum_cache);
CHECK_EQ(a->map()->instance_descriptors()->GetEnumCache(), enum_cache);
CHECK_EQ(b->map()->instance_descriptors()->GetEnumCache(), enum_cache);
CHECK_EQ(c->map()->instance_descriptors()->GetEnumCache(), enum_cache);
CHECK_EQ(enum_cache->keys()->length(), 1);
CHECK_EQ(enum_cache->indices()->length(), 1);
}
// Creating the EnumCache for {c} will create a new EnumCache on the shared
// DescriptorArray.
Handle<EnumCache> previous_enum_cache(
a->map()->instance_descriptors()->GetEnumCache());
Handle<FixedArray> previous_keys(previous_enum_cache->keys());
Handle<FixedArray> previous_indices(previous_enum_cache->indices());
CompileRun("var s = 0; for (let key in c) { s += c[key] };");
{
CHECK_EQ(a->map()->EnumLength(), 1);
CHECK_EQ(b->map()->EnumLength(), kInvalidEnumCacheSentinel);
CHECK_EQ(c->map()->EnumLength(), 3);
CHECK_EQ(cc->map()->EnumLength(), 3);
EnumCache* enum_cache = c->map()->instance_descriptors()->GetEnumCache();
CHECK_NE(enum_cache, *factory->empty_enum_cache());
// The keys and indices caches are updated.
CHECK_EQ(enum_cache, *previous_enum_cache);
CHECK_NE(enum_cache->keys(), *previous_keys);
CHECK_NE(enum_cache->indices(), *previous_indices);
CHECK_EQ(previous_keys->length(), 1);
CHECK_EQ(previous_indices->length(), 1);
CHECK_EQ(enum_cache->keys()->length(), 3);
CHECK_EQ(enum_cache->indices()->length(), 3);
// The enum cache is shared on the descriptor array of maps {a}, {b} and
// {c} only.
CHECK_NE(cc->map()->instance_descriptors()->GetEnumCache(),
*factory->empty_enum_cache());
CHECK_NE(cc->map()->instance_descriptors()->GetEnumCache(), enum_cache);
CHECK_NE(cc->map()->instance_descriptors()->GetEnumCache(),
*previous_enum_cache);
CHECK_EQ(a->map()->instance_descriptors()->GetEnumCache(), enum_cache);
CHECK_EQ(b->map()->instance_descriptors()->GetEnumCache(), enum_cache);
CHECK_EQ(c->map()->instance_descriptors()->GetEnumCache(), enum_cache);
}
// {b} can reuse the existing EnumCache, hence we only need to set the correct
// EnumLength on the map without modifying the cache itself.
previous_enum_cache =
handle(a->map()->instance_descriptors()->GetEnumCache());
previous_keys = handle(previous_enum_cache->keys());
previous_indices = handle(previous_enum_cache->indices());
CompileRun("var s = 0; for (let key in b) { s += b[key] };");
{
CHECK_EQ(a->map()->EnumLength(), 1);
CHECK_EQ(b->map()->EnumLength(), 2);
CHECK_EQ(c->map()->EnumLength(), 3);
CHECK_EQ(cc->map()->EnumLength(), 3);
EnumCache* enum_cache = c->map()->instance_descriptors()->GetEnumCache();
CHECK_NE(enum_cache, *factory->empty_enum_cache());
// The keys and indices caches are not updated.
CHECK_EQ(enum_cache, *previous_enum_cache);
CHECK_EQ(enum_cache->keys(), *previous_keys);
CHECK_EQ(enum_cache->indices(), *previous_indices);
CHECK_EQ(enum_cache->keys()->length(), 3);
CHECK_EQ(enum_cache->indices()->length(), 3);
// The enum cache is shared on the descriptor array of maps {a}, {b} and
// {c} only.
CHECK_NE(cc->map()->instance_descriptors()->GetEnumCache(),
*factory->empty_enum_cache());
CHECK_NE(cc->map()->instance_descriptors()->GetEnumCache(), enum_cache);
CHECK_NE(cc->map()->instance_descriptors()->GetEnumCache(),
*previous_enum_cache);
CHECK_EQ(a->map()->instance_descriptors()->GetEnumCache(), enum_cache);
CHECK_EQ(b->map()->instance_descriptors()->GetEnumCache(), enum_cache);
CHECK_EQ(c->map()->instance_descriptors()->GetEnumCache(), enum_cache);
}
}
} // 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