v8/test/cctest/test-hashcode.cc
Frank Emrich a669705fe0 [dict-proto] Add support for ordered property dicts, pt.3
This CL adds partial support for objects whose slow mode dictionaries
are OrderedNameDictionaries. This is the case for all slow mode objects
if V8_DICT_MODE_PROTOTYPES is enabled.

In particular, this part contains the remaining fixes to runtime code,
except for the class templating logic, which follows in a later CL.

Bug: v8:7569
Change-Id: Ib4d08d7d352125709ca916dfc75018dabf71b0cd
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2540549
Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
Reviewed-by: Peter Marshall <petermarshall@chromium.org>
Reviewed-by: Marja Hölttä <marja@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Commit-Queue: Frank Emrich <emrich@google.com>
Cr-Commit-Position: refs/heads/master@{#71275}
2020-11-19 11:54:24 +00:00

327 lines
10 KiB
C++

// 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 <stdlib.h>
#include <sstream>
#include <utility>
#include "src/init/v8.h"
#include "src/objects/objects-inl.h"
#include "src/objects/objects.h"
#include "src/objects/ordered-hash-table.h"
#include "src/third_party/siphash/halfsiphash.h"
#include "src/utils/utils.h"
#include "test/cctest/cctest.h"
namespace v8 {
namespace internal {
int AddToSetAndGetHash(Isolate* isolate, Handle<JSObject> obj,
bool has_fast_properties) {
CHECK_EQ(has_fast_properties, obj->HasFastProperties());
CHECK_EQ(ReadOnlyRoots(isolate).undefined_value(), obj->GetHash());
Handle<OrderedHashSet> set = isolate->factory()->NewOrderedHashSet();
OrderedHashSet::Add(isolate, set, obj);
CHECK_EQ(has_fast_properties, obj->HasFastProperties());
return Smi::ToInt(obj->GetHash());
}
int GetPropertyDictionaryHash(Handle<JSObject> obj) {
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
return obj->property_dictionary_ordered().Hash();
} else {
return obj->property_dictionary().Hash();
}
}
int GetPropertyDictionaryLength(Handle<JSObject> obj) {
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
return obj->property_dictionary_ordered().length();
} else {
return obj->property_dictionary().length();
}
}
void CheckIsDictionaryModeObject(Handle<JSObject> obj) {
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
CHECK(obj->raw_properties_or_hash().IsOrderedNameDictionary());
} else {
CHECK(obj->raw_properties_or_hash().IsNameDictionary());
}
}
void CheckFastObject(Handle<JSObject> obj, int hash) {
CHECK(obj->HasFastProperties());
CHECK(obj->raw_properties_or_hash().IsPropertyArray());
CHECK_EQ(Smi::FromInt(hash), obj->GetHash());
CHECK_EQ(hash, obj->property_array().Hash());
}
void CheckDictionaryObject(Handle<JSObject> obj, int hash) {
CHECK(!obj->HasFastProperties());
CheckIsDictionaryModeObject(obj);
CHECK_EQ(Smi::FromInt(hash), obj->GetHash());
CHECK_EQ(hash, GetPropertyDictionaryHash(obj));
}
TEST(AddHashCodeToFastObjectWithoutProperties) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
Handle<JSObject> obj =
isolate->factory()->NewJSObject(isolate->object_function());
CHECK(obj->HasFastProperties());
int hash = AddToSetAndGetHash(isolate, obj, true);
CHECK_EQ(Smi::FromInt(hash), obj->raw_properties_or_hash());
}
TEST(AddHashCodeToFastObjectWithInObjectProperties) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
const char* source = " var x = { a: 1};";
CompileRun(source);
Handle<JSObject> obj = GetGlobal<JSObject>("x");
CHECK_EQ(ReadOnlyRoots(isolate).empty_fixed_array(),
obj->raw_properties_or_hash());
int hash = AddToSetAndGetHash(isolate, obj, true);
CHECK_EQ(Smi::FromInt(hash), obj->raw_properties_or_hash());
}
TEST(AddHashCodeToFastObjectWithPropertiesArray) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
const char* source =
" var x = {}; "
" x.a = 1; x.b = 2; x.c = 3; x.d = 4; x.e = 5; ";
CompileRun(source);
Handle<JSObject> obj = GetGlobal<JSObject>("x");
CHECK(obj->HasFastProperties());
int hash = AddToSetAndGetHash(isolate, obj, true);
CheckFastObject(obj, hash);
}
TEST(AddHashCodeToSlowObject) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
Handle<JSObject> obj =
isolate->factory()->NewJSObject(isolate->object_function());
CHECK(obj->HasFastProperties());
JSObject::NormalizeProperties(isolate, obj, CLEAR_INOBJECT_PROPERTIES, 0,
"cctest/test-hashcode");
CheckIsDictionaryModeObject(obj);
int hash = AddToSetAndGetHash(isolate, obj, false);
CheckDictionaryObject(obj, hash);
}
TEST(TransitionFastWithInObjectToFastWithPropertyArray) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
const char* source =
" var x = { };"
" x.a = 1; x.b = 2; x.c = 3; x.d = 4;";
CompileRun(source);
Handle<JSObject> obj = GetGlobal<JSObject>("x");
CHECK(obj->HasFastProperties());
int hash = AddToSetAndGetHash(isolate, obj, true);
CHECK_EQ(Smi::FromInt(hash), obj->raw_properties_or_hash());
int length = obj->property_array().length();
CompileRun("x.e = 5;");
CHECK(obj->property_array().length() > length);
CheckFastObject(obj, hash);
}
TEST(TransitionFastWithPropertyArray) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
const char* source =
" var x = { };"
" x.a = 1; x.b = 2; x.c = 3; x.d = 4; x.e = 5; ";
CompileRun(source);
Handle<JSObject> obj = GetGlobal<JSObject>("x");
CHECK(obj->raw_properties_or_hash().IsPropertyArray());
int hash = AddToSetAndGetHash(isolate, obj, true);
CHECK_EQ(hash, obj->property_array().Hash());
int length = obj->property_array().length();
CompileRun("x.f = 2; x.g = 5; x.h = 2");
CHECK(obj->property_array().length() > length);
CheckFastObject(obj, hash);
}
TEST(TransitionFastWithPropertyArrayToSlow) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
const char* source =
" var x = { };"
" x.a = 1; x.b = 2; x.c = 3; x.d = 4; x.e = 5; ";
CompileRun(source);
Handle<JSObject> obj = GetGlobal<JSObject>("x");
CHECK(obj->raw_properties_or_hash().IsPropertyArray());
int hash = AddToSetAndGetHash(isolate, obj, true);
CHECK(obj->raw_properties_or_hash().IsPropertyArray());
CHECK_EQ(hash, obj->property_array().Hash());
JSObject::NormalizeProperties(isolate, obj, KEEP_INOBJECT_PROPERTIES, 0,
"cctest/test-hashcode");
CheckDictionaryObject(obj, hash);
}
TEST(TransitionSlowToSlow) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
const char* source = " var x = {}; ";
CompileRun(source);
Handle<JSObject> obj = GetGlobal<JSObject>("x");
JSObject::NormalizeProperties(isolate, obj, CLEAR_INOBJECT_PROPERTIES, 0,
"cctest/test-hashcode");
CheckIsDictionaryModeObject(obj);
int hash = AddToSetAndGetHash(isolate, obj, false);
CHECK_EQ(hash, GetPropertyDictionaryHash(obj));
int length = GetPropertyDictionaryLength(obj);
CompileRun("for(var i = 0; i < 10; i++) { x['f'+i] = i };");
CHECK(GetPropertyDictionaryLength(obj) > length);
CheckDictionaryObject(obj, hash);
}
TEST(TransitionSlowToFastWithoutProperties) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
Handle<JSObject> obj =
isolate->factory()->NewJSObject(isolate->object_function());
JSObject::NormalizeProperties(isolate, obj, CLEAR_INOBJECT_PROPERTIES, 0,
"cctest/test-hashcode");
CheckIsDictionaryModeObject(obj);
int hash = AddToSetAndGetHash(isolate, obj, false);
CHECK_EQ(hash, GetPropertyDictionaryHash(obj));
JSObject::MigrateSlowToFast(obj, 0, "cctest/test-hashcode");
CHECK_EQ(Smi::FromInt(hash), obj->GetHash());
}
TEST(TransitionSlowToFastWithPropertyArray) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
const char* source =
" var x = Object.create(null); "
" for(var i = 0; i < 10; i++) { x['f'+i] = i }; ";
CompileRun(source);
Handle<JSObject> obj = GetGlobal<JSObject>("x");
CheckIsDictionaryModeObject(obj);
int hash = AddToSetAndGetHash(isolate, obj, false);
CHECK_EQ(hash, GetPropertyDictionaryHash(obj));
JSObject::MigrateSlowToFast(obj, 0, "cctest/test-hashcode");
CheckFastObject(obj, hash);
}
namespace {
using HashFunction = uint32_t (*)(uint32_t key, uint64_t seed);
void TestIntegerHashQuality(const int samples_log2, int num_buckets_log2,
uint64_t seed, double max_var,
HashFunction hash_function) {
int samples = 1 << samples_log2;
int num_buckets = 1 << num_buckets_log2;
int mean = samples / num_buckets;
int* buckets = new int[num_buckets];
for (int i = 0; i < num_buckets; i++) buckets[i] = 0;
for (int i = 0; i < samples; i++) {
uint32_t hash = hash_function(i, seed);
buckets[hash % num_buckets]++;
}
int sum_deviation = 0;
for (int i = 0; i < num_buckets; i++) {
int deviation = abs(buckets[i] - mean);
sum_deviation += deviation * deviation;
}
delete[] buckets;
double variation_coefficient = sqrt(sum_deviation * 1.0 / num_buckets) / mean;
printf("samples: 1 << %2d, buckets: 1 << %2d, var_coeff: %0.3f\n",
samples_log2, num_buckets_log2, variation_coefficient);
CHECK_LT(variation_coefficient, max_var);
}
uint32_t HalfSipHash(uint32_t key, uint64_t seed) {
return halfsiphash(key, seed);
}
uint32_t JenkinsHash(uint32_t key, uint64_t seed) {
return ComputeLongHash(static_cast<uint64_t>(key) ^ seed);
}
uint32_t DefaultHash(uint32_t key, uint64_t seed) {
return ComputeSeededHash(key, seed);
}
} // anonymous namespace
void TestIntegerHashQuality(HashFunction hash_function) {
TestIntegerHashQuality(17, 13, 0x123456789ABCDEFU, 0.4, hash_function);
TestIntegerHashQuality(16, 12, 0x123456789ABCDEFU, 0.4, hash_function);
TestIntegerHashQuality(15, 11, 0xFEDCBA987654321U, 0.4, hash_function);
TestIntegerHashQuality(14, 10, 0xFEDCBA987654321U, 0.4, hash_function);
TestIntegerHashQuality(13, 9, 1, 0.4, hash_function);
TestIntegerHashQuality(12, 8, 1, 0.4, hash_function);
TestIntegerHashQuality(17, 10, 0x123456789ABCDEFU, 0.2, hash_function);
TestIntegerHashQuality(16, 9, 0x123456789ABCDEFU, 0.2, hash_function);
TestIntegerHashQuality(15, 8, 0xFEDCBA987654321U, 0.2, hash_function);
TestIntegerHashQuality(14, 7, 0xFEDCBA987654321U, 0.2, hash_function);
TestIntegerHashQuality(13, 6, 1, 0.2, hash_function);
TestIntegerHashQuality(12, 5, 1, 0.2, hash_function);
}
TEST(HalfSipHashQuality) { TestIntegerHashQuality(HalfSipHash); }
TEST(JenkinsHashQuality) { TestIntegerHashQuality(JenkinsHash); }
TEST(DefaultHashQuality) { TestIntegerHashQuality(DefaultHash); }
} // namespace internal
} // namespace v8